[439] | 1 | #ifndef MRB_WITHOUT_FLOAT
|
---|
| 2 | #if defined(MRB_DISABLE_STDIO) || defined(_WIN32) || defined(_WIN64)
|
---|
[270] | 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 |
|
---|
[331] | 37 | #include <mruby.h>
|
---|
| 38 | #include <mruby/string.h>
|
---|
[270] | 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
|
---|
[439] | 65 | pad(struct fmt_args *f, char c, ptrdiff_t w, ptrdiff_t l, uint8_t fl)
|
---|
[270] | 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
|
---|
[439] | 95 | fmt_fp(struct fmt_args *f, long double y, ptrdiff_t p, uint8_t fl, int t)
|
---|
[270] | 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;
|
---|
[439] | 101 | int e2=0, e, j;
|
---|
| 102 | ptrdiff_t l;
|
---|
[270] | 103 | char buf[9+LDBL_MANT_DIG/4], *s;
|
---|
| 104 | const char *prefix="-0X+0X 0X-0x+0x 0x";
|
---|
[439] | 105 | ptrdiff_t pl;
|
---|
[270] | 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";
|
---|
[439] | 120 | pad(f, ' ', 0, 3+pl, fl&~ZERO_PAD);
|
---|
[270] | 121 | out(f, prefix, pl);
|
---|
| 122 | out(f, ss, 3);
|
---|
[439] | 123 | pad(f, ' ', 0, 3+pl, fl^LEFT_ADJ);
|
---|
| 124 | return 3+(int)pl;
|
---|
[270] | 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;
|
---|
[439] | 132 | ptrdiff_t re;
|
---|
[270] | 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 {
|
---|
[331] | 161 | int x=(int)y;
|
---|
[270] | 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 |
|
---|
[439] | 172 | pad(f, ' ', 0, pl+l, fl);
|
---|
[270] | 173 | out(f, prefix, pl);
|
---|
[439] | 174 | pad(f, '0', 0, pl+l, fl^ZERO_PAD);
|
---|
[270] | 175 | out(f, buf, s-buf);
|
---|
| 176 | pad(f, '0', l-(ebuf-estr)-(s-buf), 0, 0);
|
---|
| 177 | out(f, estr, ebuf-estr);
|
---|
[439] | 178 | pad(f, ' ', 0, pl+l, fl^LEFT_ADJ);
|
---|
| 179 | return (int)pl+(int)l;
|
---|
[270] | 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 {
|
---|
[331] | 189 | *z = (uint32_t)y;
|
---|
[270] | 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;
|
---|
[331] | 199 | carry = (uint32_t)(x / 1000000000);
|
---|
[270] | 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;
|
---|
[439] | 207 | int sh=MIN(9,-e2), need=1+((int)p+LDBL_MANT_DIG/3+8)/9;
|
---|
[270] | 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 |
|
---|
[439] | 221 | if (a<z) for (i=10, e=9*(int)(r-a); *a>=i; i*=10, e++);
|
---|
[270] | 222 | else e=0;
|
---|
| 223 |
|
---|
| 224 | /* Perform rounding: j is precision after the radix (possibly neg) */
|
---|
[439] | 225 | j = (int)p - ((t|32)!='f')*e - ((t|32)=='g' && p);
|
---|
[270] | 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 | }
|
---|
[439] | 252 | for (i=10, e=9*(int)(r-a); *a>=i; i*=10, e++);
|
---|
[270] | 253 | }
|
---|
| 254 | }
|
---|
| 255 | if (z>d+1) z=d+1;
|
---|
| 256 | }
|
---|
| 257 | for (; z>a && !z[-1]; z--);
|
---|
[331] | 258 |
|
---|
[270] | 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 |
|
---|
[439] | 291 | pad(f, ' ', 0, pl+l, fl);
|
---|
[270] | 292 | out(f, prefix, pl);
|
---|
[439] | 293 | pad(f, '0', 0, pl+l, fl^ZERO_PAD);
|
---|
[270] | 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));
|
---|
[439] | 322 | p -= (int)(buf+9-ss);
|
---|
[270] | 323 | }
|
---|
| 324 | pad(f, '0', p+18, 18, 0);
|
---|
| 325 | out(f, estr, ebuf-estr);
|
---|
| 326 | }
|
---|
| 327 |
|
---|
[439] | 328 | pad(f, ' ', 0, pl+l, fl^LEFT_ADJ);
|
---|
[270] | 329 |
|
---|
[439] | 330 | return (int)pl+(int)l;
|
---|
[270] | 331 | }
|
---|
| 332 |
|
---|
| 333 | static int
|
---|
| 334 | fmt_core(struct fmt_args *f, const char *fmt, mrb_float flo)
|
---|
| 335 | {
|
---|
[439] | 336 | ptrdiff_t p;
|
---|
[270] | 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':
|
---|
[439] | 356 | return fmt_fp(f, flo, p, 0, *fmt);
|
---|
[270] | 357 | default:
|
---|
| 358 | return -1;
|
---|
[331] | 359 | }
|
---|
[270] | 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;
|
---|
[439] | 368 | f.str = mrb_str_new_capa(mrb, 24);
|
---|
[270] | 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 | }
|
---|
[439] | 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
|
---|