1 | /*
|
---|
2 |
|
---|
3 | Most code in this file originates from musl (src/stdio/vfprintf.c)
|
---|
4 | which, just like mruby itself, is licensed under the MIT license.
|
---|
5 |
|
---|
6 | Copyright (c) 2005-2014 Rich Felker, et al.
|
---|
7 |
|
---|
8 | Permission is hereby granted, free of charge, to any person obtaining
|
---|
9 | a copy of this software and associated documentation files (the
|
---|
10 | "Software"), to deal in the Software without restriction, including
|
---|
11 | without limitation the rights to use, copy, modify, merge, publish,
|
---|
12 | distribute, sublicense, and/or sell copies of the Software, and to
|
---|
13 | permit persons to whom the Software is furnished to do so, subject to
|
---|
14 | the following conditions:
|
---|
15 |
|
---|
16 | The above copyright notice and this permission notice shall be
|
---|
17 | included in all copies or substantial portions of the Software.
|
---|
18 |
|
---|
19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
---|
20 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
---|
21 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
---|
22 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
---|
23 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
---|
24 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
---|
25 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
---|
26 |
|
---|
27 | */
|
---|
28 |
|
---|
29 | #include <limits.h>
|
---|
30 | #include <string.h>
|
---|
31 | #include <stdint.h>
|
---|
32 | #include <math.h>
|
---|
33 | #include <float.h>
|
---|
34 | #include <ctype.h>
|
---|
35 |
|
---|
36 | #include <mruby.h>
|
---|
37 | #include <mruby/string.h>
|
---|
38 |
|
---|
39 | struct fmt_args {
|
---|
40 | mrb_state *mrb;
|
---|
41 | mrb_value str;
|
---|
42 | };
|
---|
43 |
|
---|
44 | #define MAX(a,b) ((a)>(b) ? (a) : (b))
|
---|
45 | #define MIN(a,b) ((a)<(b) ? (a) : (b))
|
---|
46 |
|
---|
47 | /* Convenient bit representation for modifier flags, which all fall
|
---|
48 | * within 31 codepoints of the space character. */
|
---|
49 |
|
---|
50 | #define ALT_FORM (1U<<('#'-' '))
|
---|
51 | #define ZERO_PAD (1U<<('0'-' '))
|
---|
52 | #define LEFT_ADJ (1U<<('-'-' '))
|
---|
53 | #define PAD_POS (1U<<(' '-' '))
|
---|
54 | #define MARK_POS (1U<<('+'-' '))
|
---|
55 |
|
---|
56 | static void
|
---|
57 | out(struct fmt_args *f, const char *s, size_t l)
|
---|
58 | {
|
---|
59 | mrb_str_cat(f->mrb, f->str, s, l);
|
---|
60 | }
|
---|
61 |
|
---|
62 | #define PAD_SIZE 256
|
---|
63 | static void
|
---|
64 | pad(struct fmt_args *f, char c, int w, int l, int fl)
|
---|
65 | {
|
---|
66 | char pad[PAD_SIZE];
|
---|
67 | if (fl & (LEFT_ADJ | ZERO_PAD) || l >= w) return;
|
---|
68 | l = w - l;
|
---|
69 | memset(pad, c, l>PAD_SIZE ? PAD_SIZE : l);
|
---|
70 | for (; l >= PAD_SIZE; l -= PAD_SIZE)
|
---|
71 | out(f, pad, PAD_SIZE);
|
---|
72 | out(f, pad, l);
|
---|
73 | }
|
---|
74 |
|
---|
75 | static const char xdigits[16] = {
|
---|
76 | '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
|
---|
77 | };
|
---|
78 |
|
---|
79 | static char*
|
---|
80 | fmt_u(uint32_t x, char *s)
|
---|
81 | {
|
---|
82 | for (; x; x /= 10) *--s = '0' + x % 10;
|
---|
83 | return s;
|
---|
84 | }
|
---|
85 |
|
---|
86 | /* Do not override this check. The floating point printing code below
|
---|
87 | * depends on the float.h constants being right. If they are wrong, it
|
---|
88 | * may overflow the stack. */
|
---|
89 | #if LDBL_MANT_DIG == 53
|
---|
90 | typedef char compiler_defines_long_double_incorrectly[9-(int)sizeof(long double)];
|
---|
91 | #endif
|
---|
92 |
|
---|
93 | static int
|
---|
94 | fmt_fp(struct fmt_args *f, long double y, int w, int p, int fl, int t)
|
---|
95 | {
|
---|
96 | uint32_t big[(LDBL_MANT_DIG+28)/29 + 1 // mantissa expansion
|
---|
97 | + (LDBL_MAX_EXP+LDBL_MANT_DIG+28+8)/9]; // exponent expansion
|
---|
98 | uint32_t *a, *d, *r, *z;
|
---|
99 | uint32_t i;
|
---|
100 | int e2=0, e, j, l;
|
---|
101 | char buf[9+LDBL_MANT_DIG/4], *s;
|
---|
102 | const char *prefix="-0X+0X 0X-0x+0x 0x";
|
---|
103 | int pl;
|
---|
104 | char ebuf0[3*sizeof(int)], *ebuf=&ebuf0[3*sizeof(int)], *estr;
|
---|
105 |
|
---|
106 | pl=1;
|
---|
107 | if (signbit(y)) {
|
---|
108 | y=-y;
|
---|
109 | } else if (fl & MARK_POS) {
|
---|
110 | prefix+=3;
|
---|
111 | } else if (fl & PAD_POS) {
|
---|
112 | prefix+=6;
|
---|
113 | } else prefix++, pl=0;
|
---|
114 |
|
---|
115 | if (!isfinite(y)) {
|
---|
116 | const char *ss = (t&32)?"inf":"INF";
|
---|
117 | if (y!=y) ss=(t&32)?"nan":"NAN";
|
---|
118 | pad(f, ' ', w, 3+pl, fl&~ZERO_PAD);
|
---|
119 | out(f, prefix, pl);
|
---|
120 | out(f, ss, 3);
|
---|
121 | pad(f, ' ', w, 3+pl, fl^LEFT_ADJ);
|
---|
122 | return MAX(w, 3+pl);
|
---|
123 | }
|
---|
124 |
|
---|
125 | y = frexp((double)y, &e2) * 2;
|
---|
126 | if (y) e2--;
|
---|
127 |
|
---|
128 | if ((t|32)=='a') {
|
---|
129 | long double round = 8.0;
|
---|
130 | int re;
|
---|
131 |
|
---|
132 | if (t&32) prefix += 9;
|
---|
133 | pl += 2;
|
---|
134 |
|
---|
135 | if (p<0 || p>=LDBL_MANT_DIG/4-1) re=0;
|
---|
136 | else re=LDBL_MANT_DIG/4-1-p;
|
---|
137 |
|
---|
138 | if (re) {
|
---|
139 | while (re--) round*=16;
|
---|
140 | if (*prefix=='-') {
|
---|
141 | y=-y;
|
---|
142 | y-=round;
|
---|
143 | y+=round;
|
---|
144 | y=-y;
|
---|
145 | }
|
---|
146 | else {
|
---|
147 | y+=round;
|
---|
148 | y-=round;
|
---|
149 | }
|
---|
150 | }
|
---|
151 |
|
---|
152 | estr=fmt_u(e2<0 ? -e2 : e2, ebuf);
|
---|
153 | if (estr==ebuf) *--estr='0';
|
---|
154 | *--estr = (e2<0 ? '-' : '+');
|
---|
155 | *--estr = t+('p'-'a');
|
---|
156 |
|
---|
157 | s=buf;
|
---|
158 | do {
|
---|
159 | int x=(int)y;
|
---|
160 | *s++=xdigits[x]|(t&32);
|
---|
161 | y=16*(y-x);
|
---|
162 | if (s-buf==1 && (y||p>0||(fl&ALT_FORM))) *s++='.';
|
---|
163 | } while (y);
|
---|
164 |
|
---|
165 | if (p && s-buf-2 < p)
|
---|
166 | l = (p+2) + (ebuf-estr);
|
---|
167 | else
|
---|
168 | l = (s-buf) + (ebuf-estr);
|
---|
169 |
|
---|
170 | pad(f, ' ', w, pl+l, fl);
|
---|
171 | out(f, prefix, pl);
|
---|
172 | pad(f, '0', w, pl+l, fl^ZERO_PAD);
|
---|
173 | out(f, buf, s-buf);
|
---|
174 | pad(f, '0', l-(ebuf-estr)-(s-buf), 0, 0);
|
---|
175 | out(f, estr, ebuf-estr);
|
---|
176 | pad(f, ' ', w, pl+l, fl^LEFT_ADJ);
|
---|
177 | return MAX(w, pl+l);
|
---|
178 | }
|
---|
179 | if (p<0) p=6;
|
---|
180 |
|
---|
181 | if (y) y *= 268435456.0, e2-=28;
|
---|
182 |
|
---|
183 | if (e2<0) a=r=z=big;
|
---|
184 | else a=r=z=big+sizeof(big)/sizeof(*big) - LDBL_MANT_DIG - 1;
|
---|
185 |
|
---|
186 | do {
|
---|
187 | *z = (uint32_t)y;
|
---|
188 | y = 1000000000*(y-*z++);
|
---|
189 | } while (y);
|
---|
190 |
|
---|
191 | while (e2>0) {
|
---|
192 | uint32_t carry=0;
|
---|
193 | int sh=MIN(29,e2);
|
---|
194 | for (d=z-1; d>=a; d--) {
|
---|
195 | uint64_t x = ((uint64_t)*d<<sh)+carry;
|
---|
196 | *d = x % 1000000000;
|
---|
197 | carry = (uint32_t)(x / 1000000000);
|
---|
198 | }
|
---|
199 | if (carry) *--a = carry;
|
---|
200 | while (z>a && !z[-1]) z--;
|
---|
201 | e2-=sh;
|
---|
202 | }
|
---|
203 | while (e2<0) {
|
---|
204 | uint32_t carry=0, *b;
|
---|
205 | int sh=MIN(9,-e2), need=1+(p+LDBL_MANT_DIG/3+8)/9;
|
---|
206 | for (d=a; d<z; d++) {
|
---|
207 | uint32_t rm = *d & ((1<<sh)-1);
|
---|
208 | *d = (*d>>sh) + carry;
|
---|
209 | carry = (1000000000>>sh) * rm;
|
---|
210 | }
|
---|
211 | if (!*a) a++;
|
---|
212 | if (carry) *z++ = carry;
|
---|
213 | /* Avoid (slow!) computation past requested precision */
|
---|
214 | b = (t|32)=='f' ? r : a;
|
---|
215 | if (z-b > need) z = b+need;
|
---|
216 | e2+=sh;
|
---|
217 | }
|
---|
218 |
|
---|
219 | if (a<z) for (i=10, e=9*(r-a); *a>=i; i*=10, e++);
|
---|
220 | else e=0;
|
---|
221 |
|
---|
222 | /* Perform rounding: j is precision after the radix (possibly neg) */
|
---|
223 | j = p - ((t|32)!='f')*e - ((t|32)=='g' && p);
|
---|
224 | if (j < 9*(z-r-1)) {
|
---|
225 | uint32_t x;
|
---|
226 | /* We avoid C's broken division of negative numbers */
|
---|
227 | d = r + 1 + ((j+9*LDBL_MAX_EXP)/9 - LDBL_MAX_EXP);
|
---|
228 | j += 9*LDBL_MAX_EXP;
|
---|
229 | j %= 9;
|
---|
230 | for (i=10, j++; j<9; i*=10, j++);
|
---|
231 | x = *d % i;
|
---|
232 | /* Are there any significant digits past j? */
|
---|
233 | if (x || d+1!=z) {
|
---|
234 | long double round = 2/LDBL_EPSILON;
|
---|
235 | long double small;
|
---|
236 | if (*d/i & 1) round += 2;
|
---|
237 | if (x<i/2) small=0.5;
|
---|
238 | else if (x==i/2 && d+1==z) small=1.0;
|
---|
239 | else small=1.5;
|
---|
240 | if (pl && *prefix=='-') round*=-1, small*=-1;
|
---|
241 | *d -= x;
|
---|
242 | /* Decide whether to round by probing round+small */
|
---|
243 | if (round+small != round) {
|
---|
244 | *d = *d + i;
|
---|
245 | while (*d > 999999999) {
|
---|
246 | *d--=0;
|
---|
247 | if (d<a) *--a=0;
|
---|
248 | (*d)++;
|
---|
249 | }
|
---|
250 | for (i=10, e=9*(r-a); *a>=i; i*=10, e++);
|
---|
251 | }
|
---|
252 | }
|
---|
253 | if (z>d+1) z=d+1;
|
---|
254 | }
|
---|
255 | for (; z>a && !z[-1]; z--);
|
---|
256 |
|
---|
257 | if ((t|32)=='g') {
|
---|
258 | if (!p) p++;
|
---|
259 | if (p>e && e>=-4) {
|
---|
260 | t--;
|
---|
261 | p-=e+1;
|
---|
262 | }
|
---|
263 | else {
|
---|
264 | t-=2;
|
---|
265 | p--;
|
---|
266 | }
|
---|
267 | if (!(fl&ALT_FORM)) {
|
---|
268 | /* Count trailing zeros in last place */
|
---|
269 | if (z>a && z[-1]) for (i=10, j=0; z[-1]%i==0; i*=10, j++);
|
---|
270 | else j=9;
|
---|
271 | if ((t|32)=='f')
|
---|
272 | p = MIN(p,MAX(0,9*(z-r-1)-j));
|
---|
273 | else
|
---|
274 | p = MIN(p,MAX(0,9*(z-r-1)+e-j));
|
---|
275 | }
|
---|
276 | }
|
---|
277 | l = 1 + p + (p || (fl&ALT_FORM));
|
---|
278 | if ((t|32)=='f') {
|
---|
279 | if (e>0) l+=e;
|
---|
280 | }
|
---|
281 | else {
|
---|
282 | estr=fmt_u(e<0 ? -e : e, ebuf);
|
---|
283 | while(ebuf-estr<2) *--estr='0';
|
---|
284 | *--estr = (e<0 ? '-' : '+');
|
---|
285 | *--estr = t;
|
---|
286 | l += ebuf-estr;
|
---|
287 | }
|
---|
288 |
|
---|
289 | pad(f, ' ', w, pl+l, fl);
|
---|
290 | out(f, prefix, pl);
|
---|
291 | pad(f, '0', w, pl+l, fl^ZERO_PAD);
|
---|
292 |
|
---|
293 | if ((t|32)=='f') {
|
---|
294 | if (a>r) a=r;
|
---|
295 | for (d=a; d<=r; d++) {
|
---|
296 | char *ss = fmt_u(*d, buf+9);
|
---|
297 | if (d!=a) while (ss>buf) *--ss='0';
|
---|
298 | else if (ss==buf+9) *--ss='0';
|
---|
299 | out(f, ss, buf+9-ss);
|
---|
300 | }
|
---|
301 | if (p || (fl&ALT_FORM)) out(f, ".", 1);
|
---|
302 | for (; d<z && p>0; d++, p-=9) {
|
---|
303 | char *ss = fmt_u(*d, buf+9);
|
---|
304 | while (ss>buf) *--ss='0';
|
---|
305 | out(f, ss, MIN(9,p));
|
---|
306 | }
|
---|
307 | pad(f, '0', p+9, 9, 0);
|
---|
308 | }
|
---|
309 | else {
|
---|
310 | if (z<=a) z=a+1;
|
---|
311 | for (d=a; d<z && p>=0; d++) {
|
---|
312 | char *ss = fmt_u(*d, buf+9);
|
---|
313 | if (ss==buf+9) *--ss='0';
|
---|
314 | if (d!=a) while (ss>buf) *--ss='0';
|
---|
315 | else {
|
---|
316 | out(f, ss++, 1);
|
---|
317 | if (p>0||(fl&ALT_FORM)) out(f, ".", 1);
|
---|
318 | }
|
---|
319 | out(f, ss, MIN(buf+9-ss, p));
|
---|
320 | p -= buf+9-ss;
|
---|
321 | }
|
---|
322 | pad(f, '0', p+18, 18, 0);
|
---|
323 | out(f, estr, ebuf-estr);
|
---|
324 | }
|
---|
325 |
|
---|
326 | pad(f, ' ', w, pl+l, fl^LEFT_ADJ);
|
---|
327 |
|
---|
328 | return MAX(w, pl+l);
|
---|
329 | }
|
---|
330 |
|
---|
331 | static int
|
---|
332 | fmt_core(struct fmt_args *f, const char *fmt, mrb_float flo)
|
---|
333 | {
|
---|
334 | int p;
|
---|
335 |
|
---|
336 | if (*fmt != '%') {
|
---|
337 | return -1;
|
---|
338 | }
|
---|
339 | ++fmt;
|
---|
340 |
|
---|
341 | if (*fmt == '.') {
|
---|
342 | ++fmt;
|
---|
343 | for (p = 0; ISDIGIT(*fmt); ++fmt) {
|
---|
344 | p = 10 * p + (*fmt - '0');
|
---|
345 | }
|
---|
346 | }
|
---|
347 | else {
|
---|
348 | p = -1;
|
---|
349 | }
|
---|
350 |
|
---|
351 | switch (*fmt) {
|
---|
352 | case 'e': case 'f': case 'g': case 'a':
|
---|
353 | case 'E': case 'F': case 'G': case 'A':
|
---|
354 | return fmt_fp(f, flo, 0, p, 0, *fmt);
|
---|
355 | default:
|
---|
356 | return -1;
|
---|
357 | }
|
---|
358 | }
|
---|
359 |
|
---|
360 | mrb_value
|
---|
361 | mrb_float_to_str(mrb_state *mrb, mrb_value flo, const char *fmt)
|
---|
362 | {
|
---|
363 | struct fmt_args f;
|
---|
364 |
|
---|
365 | f.mrb = mrb;
|
---|
366 | f.str = mrb_str_buf_new(mrb, 24);
|
---|
367 | if (fmt_core(&f, fmt, mrb_float(flo)) < 0) {
|
---|
368 | mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid format string");
|
---|
369 | }
|
---|
370 | return f.str;
|
---|
371 | }
|
---|