1 #ifndef MRB_WITHOUT_FLOAT
2 #if defined(MRB_DISABLE_STDIO) || defined(_WIN32) || defined(_WIN64)
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.
8 Copyright (c) 2005-2014 Rich Felker, et al.
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:
18 The above copyright notice and this permission notice shall be
19 included in all copies or substantial portions of the Software.
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.
39 #include <mruby/string.h>
46 #define MAX(a,b) ((a)>(b) ? (a) : (b))
47 #define MIN(a,b) ((a)<(b) ? (a) : (b))
49 /* Convenient bit representation for modifier flags, which all fall
50 * within 31 codepoints of the space character. */
52 #define ALT_FORM (1U<<('#'-' '))
53 #define ZERO_PAD (1U<<('0'-' '))
54 #define LEFT_ADJ (1U<<('-'-' '))
55 #define PAD_POS (1U<<(' '-' '))
56 #define MARK_POS (1U<<('+'-' '))
59 out(struct fmt_args *f, const char *s, size_t l)
61 mrb_str_cat(f->mrb, f->str, s, l);
66 pad(struct fmt_args *f, char c, ptrdiff_t w, ptrdiff_t l, uint8_t fl)
69 if (fl & (LEFT_ADJ | ZERO_PAD) || l >= w) return;
71 memset(pad, c, l>PAD_SIZE ? PAD_SIZE : l);
72 for (; l >= PAD_SIZE; l -= PAD_SIZE)
73 out(f, pad, PAD_SIZE);
77 static const char xdigits[16] = {
78 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
82 fmt_u(uint32_t x, char *s)
84 for (; x; x /= 10) *--s = '0' + x % 10;
88 /* Do not override this check. The floating point printing code below
89 * depends on the float.h constants being right. If they are wrong, it
90 * may overflow the stack. */
91 #if LDBL_MANT_DIG == 53
92 typedef char compiler_defines_long_double_incorrectly[9-(int)sizeof(long double)];
96 fmt_fp(struct fmt_args *f, long double y, ptrdiff_t p, uint8_t fl, int t)
98 uint32_t big[(LDBL_MANT_DIG+28)/29 + 1 // mantissa expansion
99 + (LDBL_MAX_EXP+LDBL_MANT_DIG+28+8)/9]; // exponent expansion
100 uint32_t *a, *d, *r, *z;
104 char buf[9+LDBL_MANT_DIG/4], *s;
105 const char *prefix="-0X+0X 0X-0x+0x 0x";
107 char ebuf0[3*sizeof(int)], *ebuf=&ebuf0[3*sizeof(int)], *estr;
112 } else if (fl & MARK_POS) {
114 } else if (fl & PAD_POS) {
116 } else prefix++, pl=0;
119 const char *ss = (t&32)?"inf":"INF";
120 if (y!=y) ss=(t&32)?"nan":"NAN";
121 pad(f, ' ', 0, 3+pl, fl&~ZERO_PAD);
124 pad(f, ' ', 0, 3+pl, fl^LEFT_ADJ);
128 y = frexp((double)y, &e2) * 2;
132 long double round = 8.0;
135 if (t&32) prefix += 9;
138 if (p<0 || p>=LDBL_MANT_DIG/4-1) re=0;
139 else re=LDBL_MANT_DIG/4-1-p;
142 while (re--) round*=16;
155 estr=fmt_u(e2<0 ? -e2 : e2, ebuf);
156 if (estr==ebuf) *--estr='0';
157 *--estr = (e2<0 ? '-' : '+');
158 *--estr = t+('p'-'a');
163 *s++=xdigits[x]|(t&32);
165 if (s-buf==1 && (y||p>0||(fl&ALT_FORM))) *s++='.';
168 if (p && s-buf-2 < p)
169 l = (p+2) + (ebuf-estr);
171 l = (s-buf) + (ebuf-estr);
173 pad(f, ' ', 0, pl+l, fl);
175 pad(f, '0', 0, pl+l, fl^ZERO_PAD);
177 pad(f, '0', l-(ebuf-estr)-(s-buf), 0, 0);
178 out(f, estr, ebuf-estr);
179 pad(f, ' ', 0, pl+l, fl^LEFT_ADJ);
180 return (int)pl+(int)l;
184 if (y) y *= 268435456.0, e2-=28;
187 else a=r=z=big+sizeof(big)/sizeof(*big) - LDBL_MANT_DIG - 1;
191 y = 1000000000*(y-*z++);
197 for (d=z-1; d>=a; d--) {
198 uint64_t x = ((uint64_t)*d<<sh)+carry;
200 carry = (uint32_t)(x / 1000000000);
202 if (carry) *--a = carry;
203 while (z>a && !z[-1]) z--;
207 uint32_t carry=0, *b;
208 int sh=MIN(9,-e2), need=1+((int)p+LDBL_MANT_DIG/3+8)/9;
209 for (d=a; d<z; d++) {
210 uint32_t rm = *d & ((1<<sh)-1);
211 *d = (*d>>sh) + carry;
212 carry = (1000000000>>sh) * rm;
215 if (carry) *z++ = carry;
216 /* Avoid (slow!) computation past requested precision */
217 b = (t|32)=='f' ? r : a;
218 if (z-b > need) z = b+need;
222 if (a<z) for (i=10, e=9*(int)(r-a); *a>=i; i*=10, e++);
225 /* Perform rounding: j is precision after the radix (possibly neg) */
226 j = (int)p - ((t|32)!='f')*e - ((t|32)=='g' && p);
229 /* We avoid C's broken division of negative numbers */
230 d = r + 1 + ((j+9*LDBL_MAX_EXP)/9 - LDBL_MAX_EXP);
233 for (i=10, j++; j<9; i*=10, j++);
235 /* Are there any significant digits past j? */
237 long double round = 2/LDBL_EPSILON;
239 if (*d/i & 1) round += 2;
240 if (x<i/2) small=0.5;
241 else if (x==i/2 && d+1==z) small=1.0;
243 if (pl && *prefix=='-') round*=-1, small*=-1;
245 /* Decide whether to round by probing round+small */
246 if (round+small != round) {
248 while (*d > 999999999) {
253 for (i=10, e=9*(int)(r-a); *a>=i; i*=10, e++);
258 for (; z>a && !z[-1]; z--);
270 if (!(fl&ALT_FORM)) {
271 /* Count trailing zeros in last place */
272 if (z>a && z[-1]) for (i=10, j=0; z[-1]%i==0; i*=10, j++);
275 p = MIN(p,MAX(0,9*(z-r-1)-j));
277 p = MIN(p,MAX(0,9*(z-r-1)+e-j));
280 l = 1 + p + (p || (fl&ALT_FORM));
285 estr=fmt_u(e<0 ? -e : e, ebuf);
286 while(ebuf-estr<2) *--estr='0';
287 *--estr = (e<0 ? '-' : '+');
292 pad(f, ' ', 0, pl+l, fl);
294 pad(f, '0', 0, pl+l, fl^ZERO_PAD);
298 for (d=a; d<=r; d++) {
299 char *ss = fmt_u(*d, buf+9);
300 if (d!=a) while (ss>buf) *--ss='0';
301 else if (ss==buf+9) *--ss='0';
302 out(f, ss, buf+9-ss);
304 if (p || (fl&ALT_FORM)) out(f, ".", 1);
305 for (; d<z && p>0; d++, p-=9) {
306 char *ss = fmt_u(*d, buf+9);
307 while (ss>buf) *--ss='0';
308 out(f, ss, MIN(9,p));
310 pad(f, '0', p+9, 9, 0);
314 for (d=a; d<z && p>=0; d++) {
315 char *ss = fmt_u(*d, buf+9);
316 if (ss==buf+9) *--ss='0';
317 if (d!=a) while (ss>buf) *--ss='0';
320 if (p>0||(fl&ALT_FORM)) out(f, ".", 1);
322 out(f, ss, MIN(buf+9-ss, p));
323 p -= (int)(buf+9-ss);
325 pad(f, '0', p+18, 18, 0);
326 out(f, estr, ebuf-estr);
329 pad(f, ' ', 0, pl+l, fl^LEFT_ADJ);
331 return (int)pl+(int)l;
335 fmt_core(struct fmt_args *f, const char *fmt, mrb_float flo)
346 for (p = 0; ISDIGIT(*fmt); ++fmt) {
347 p = 10 * p + (*fmt - '0');
355 case 'e': case 'f': case 'g': case 'a':
356 case 'E': case 'F': case 'G': case 'A':
357 return fmt_fp(f, flo, p, 0, *fmt);
364 mrb_float_to_str(mrb_state *mrb, mrb_value flo, const char *fmt)
369 f.str = mrb_str_new_capa(mrb, 24);
370 if (fmt_core(&f, fmt, mrb_float(flo)) < 0) {
371 mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid format string");
375 #else /* MRB_DISABLE_STDIO */
380 mrb_float_to_str(mrb_state *mrb, mrb_value flo, const char *fmt)
384 snprintf(buf, sizeof(buf), fmt, mrb_float(flo));
385 return mrb_str_new_cstr(mrb, buf);
387 #endif /* MRB_DISABLE_STDIO */