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