--- /dev/null
+/*
+ * Tiny float64 printing and parsing library
+ *
+ * Copyright (c) 2024 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <inttypes.h>
+#include <string.h>
+#include <assert.h>
+#include <ctype.h>
+#include <sys/time.h>
+#include <math.h>
+#include <setjmp.h>
+
+#include "cutils.h"
+#include "dtoa.h"
+
+/*
+ TODO:
+ - test n_digits=101 instead of 100
+ - simplify subnormal handling
+ - reduce max memory usage
+ - free format: could add shortcut if exact result
+ - use 64 bit limb_t when possible
+ - use another algorithm for free format dtoa in base 10 (ryu ?)
+*/
+
+#define USE_POW5_TABLE
+/* use fast path to print small integers in free format */
+#define USE_FAST_INT
+
+#define LIMB_LOG2_BITS 5
+
+#define LIMB_BITS (1 << LIMB_LOG2_BITS)
+
+typedef int32_t slimb_t;
+typedef uint32_t limb_t;
+typedef uint64_t dlimb_t;
+
+#define LIMB_DIGITS 9
+
+#define JS_RADIX_MAX 36
+
+#define DBIGNUM_LEN_MAX 52 /* ~ 2^(1072+53)*36^100 (dtoa) */
+#define MANT_LEN_MAX 18 /* < 36^100 */
+
+typedef intptr_t mp_size_t;
+
+/* the represented number is sum(i, tab[i]*2^(LIMB_BITS * i)) */
+typedef struct {
+ int len; /* >= 1 */
+ limb_t tab[];
+} mpb_t;
+
+static limb_t mp_add_ui(limb_t *tab, limb_t b, size_t n)
+{
+ size_t i;
+ limb_t k, a;
+
+ k=b;
+ for(i=0;i<n;i++) {
+ if (k == 0)
+ break;
+ a = tab[i] + k;
+ k = (a < k);
+ tab[i] = a;
+ }
+ return k;
+}
+
+/* tabr[] = taba[] * b + l. Return the high carry */
+static limb_t mp_mul1(limb_t *tabr, const limb_t *taba, limb_t n,
+ limb_t b, limb_t l)
+{
+ limb_t i;
+ dlimb_t t;
+
+ for(i = 0; i < n; i++) {
+ t = (dlimb_t)taba[i] * (dlimb_t)b + l;
+ tabr[i] = t;
+ l = t >> LIMB_BITS;
+ }
+ return l;
+}
+
+/* WARNING: d must be >= 2^(LIMB_BITS-1) */
+static inline limb_t udiv1norm_init(limb_t d)
+{
+ limb_t a0, a1;
+ a1 = -d - 1;
+ a0 = -1;
+ return (((dlimb_t)a1 << LIMB_BITS) | a0) / d;
+}
+
+/* return the quotient and the remainder in '*pr'of 'a1*2^LIMB_BITS+a0
+ / d' with 0 <= a1 < d. */
+static inline limb_t udiv1norm(limb_t *pr, limb_t a1, limb_t a0,
+ limb_t d, limb_t d_inv)
+{
+ limb_t n1m, n_adj, q, r, ah;
+ dlimb_t a;
+ n1m = ((slimb_t)a0 >> (LIMB_BITS - 1));
+ n_adj = a0 + (n1m & d);
+ a = (dlimb_t)d_inv * (a1 - n1m) + n_adj;
+ q = (a >> LIMB_BITS) + a1;
+ /* compute a - q * r and update q so that the remainder is between
+ 0 and d - 1 */
+ a = ((dlimb_t)a1 << LIMB_BITS) | a0;
+ a = a - (dlimb_t)q * d - d;
+ ah = a >> LIMB_BITS;
+ q += 1 + ah;
+ r = (limb_t)a + (ah & d);
+ *pr = r;
+ return q;
+}
+
+static limb_t mp_div1(limb_t *tabr, const limb_t *taba, limb_t n,
+ limb_t b, limb_t r)
+{
+ slimb_t i;
+ dlimb_t a1;
+ for(i = n - 1; i >= 0; i--) {
+ a1 = ((dlimb_t)r << LIMB_BITS) | taba[i];
+ tabr[i] = a1 / b;
+ r = a1 % b;
+ }
+ return r;
+}
+
+/* r = (a + high*B^n) >> shift. Return the remainder r (0 <= r < 2^shift).
+ 1 <= shift <= LIMB_BITS - 1 */
+static limb_t mp_shr(limb_t *tab_r, const limb_t *tab, mp_size_t n,
+ int shift, limb_t high)
+{
+ mp_size_t i;
+ limb_t l, a;
+
+ assert(shift >= 1 && shift < LIMB_BITS);
+ l = high;
+ for(i = n - 1; i >= 0; i--) {
+ a = tab[i];
+ tab_r[i] = (a >> shift) | (l << (LIMB_BITS - shift));
+ l = a;
+ }
+ return l & (((limb_t)1 << shift) - 1);
+}
+
+/* r = (a << shift) + low. 1 <= shift <= LIMB_BITS - 1, 0 <= low <
+ 2^shift. */
+static limb_t mp_shl(limb_t *tab_r, const limb_t *tab, mp_size_t n,
+ int shift, limb_t low)
+{
+ mp_size_t i;
+ limb_t l, a;
+
+ assert(shift >= 1 && shift < LIMB_BITS);
+ l = low;
+ for(i = 0; i < n; i++) {
+ a = tab[i];
+ tab_r[i] = (a << shift) | l;
+ l = (a >> (LIMB_BITS - shift));
+ }
+ return l;
+}
+
+static no_inline limb_t mp_div1norm(limb_t *tabr, const limb_t *taba, limb_t n,
+ limb_t b, limb_t r, limb_t b_inv, int shift)
+{
+ slimb_t i;
+
+ if (shift != 0) {
+ r = (r << shift) | mp_shl(tabr, taba, n, shift, 0);
+ }
+ for(i = n - 1; i >= 0; i--) {
+ tabr[i] = udiv1norm(&r, r, taba[i], b, b_inv);
+ }
+ r >>= shift;
+ return r;
+}
+
+static __maybe_unused void mpb_dump(const char *str, const mpb_t *a)
+{
+ int i;
+
+ printf("%s= 0x", str);
+ for(i = a->len - 1; i >= 0; i--) {
+ printf("%08x", a->tab[i]);
+ if (i != 0)
+ printf("_");
+ }
+ printf("\n");
+}
+
+static void mpb_renorm(mpb_t *r)
+{
+ while (r->len > 1 && r->tab[r->len - 1] == 0)
+ r->len--;
+}
+
+#ifdef USE_POW5_TABLE
+static const uint32_t pow5_table[17] = {
+ 0x00000005, 0x00000019, 0x0000007d, 0x00000271,
+ 0x00000c35, 0x00003d09, 0x0001312d, 0x0005f5e1,
+ 0x001dcd65, 0x009502f9, 0x02e90edd, 0x0e8d4a51,
+ 0x48c27395, 0x6bcc41e9, 0x1afd498d, 0x86f26fc1,
+ 0xa2bc2ec5,
+};
+
+static const uint8_t pow5h_table[4] = {
+ 0x00000001, 0x00000007, 0x00000023, 0x000000b1,
+};
+
+static const uint32_t pow5_inv_table[13] = {
+ 0x99999999, 0x47ae147a, 0x0624dd2f, 0xa36e2eb1,
+ 0x4f8b588e, 0x0c6f7a0b, 0xad7f29ab, 0x5798ee23,
+ 0x12e0be82, 0xb7cdfd9d, 0x5fd7fe17, 0x19799812,
+ 0xc25c2684,
+};
+#endif
+
+/* return a^b */
+static uint64_t pow_ui(uint32_t a, uint32_t b)
+{
+ int i, n_bits;
+ uint64_t r;
+ if (b == 0)
+ return 1;
+ if (b == 1)
+ return a;
+#ifdef USE_POW5_TABLE
+ if ((a == 5 || a == 10) && b <= 17) {
+ r = pow5_table[b - 1];
+ if (b >= 14) {
+ r |= (uint64_t)pow5h_table[b - 14] << 32;
+ }
+ if (a == 10)
+ r <<= b;
+ return r;
+ }
+#endif
+ r = a;
+ n_bits = 32 - clz32(b);
+ for(i = n_bits - 2; i >= 0; i--) {
+ r *= r;
+ if ((b >> i) & 1)
+ r *= a;
+ }
+ return r;
+}
+
+static uint32_t pow_ui_inv(uint32_t *pr_inv, int *pshift, uint32_t a, uint32_t b)
+{
+ uint32_t r_inv, r;
+ int shift;
+#ifdef USE_POW5_TABLE
+ if (a == 5 && b >= 1 && b <= 13) {
+ r = pow5_table[b - 1];
+ shift = clz32(r);
+ r <<= shift;
+ r_inv = pow5_inv_table[b - 1];
+ } else
+#endif
+ {
+ r = pow_ui(a, b);
+ shift = clz32(r);
+ r <<= shift;
+ r_inv = udiv1norm_init(r);
+ }
+ *pshift = shift;
+ *pr_inv = r_inv;
+ return r;
+}
+
+enum {
+ JS_RNDN, /* round to nearest, ties to even */
+ JS_RNDNA, /* round to nearest, ties away from zero */
+ JS_RNDZ,
+};
+
+static int mpb_get_bit(const mpb_t *r, int k)
+{
+ int l;
+
+ l = (unsigned)k / LIMB_BITS;
+ k = k & (LIMB_BITS - 1);
+ if (l >= r->len)
+ return 0;
+ else
+ return (r->tab[l] >> k) & 1;
+}
+
+/* compute round(r / 2^shift). 'shift' can be negative */
+static void mpb_shr_round(mpb_t *r, int shift, int rnd_mode)
+{
+ int l, i;
+
+ if (shift == 0)
+ return;
+ if (shift < 0) {
+ shift = -shift;
+ l = (unsigned)shift / LIMB_BITS;
+ shift = shift & (LIMB_BITS - 1);
+ if (shift != 0) {
+ r->tab[r->len] = mp_shl(r->tab, r->tab, r->len, shift, 0);
+ r->len++;
+ mpb_renorm(r);
+ }
+ if (l > 0) {
+ for(i = r->len - 1; i >= 0; i--)
+ r->tab[i + l] = r->tab[i];
+ for(i = 0; i < l; i++)
+ r->tab[i] = 0;
+ r->len += l;
+ }
+ } else {
+ limb_t bit1, bit2;
+ int k, add_one;
+
+ switch(rnd_mode) {
+ default:
+ case JS_RNDZ:
+ add_one = 0;
+ break;
+ case JS_RNDN:
+ case JS_RNDNA:
+ bit1 = mpb_get_bit(r, shift - 1);
+ if (bit1) {
+ if (rnd_mode == JS_RNDNA) {
+ bit2 = 1;
+ } else {
+ /* bit2 = oring of all the bits after bit1 */
+ bit2 = 0;
+ if (shift >= 2) {
+ k = shift - 1;
+ l = (unsigned)k / LIMB_BITS;
+ k = k & (LIMB_BITS - 1);
+ for(i = 0; i < min_int(l, r->len); i++)
+ bit2 |= r->tab[i];
+ if (l < r->len)
+ bit2 |= r->tab[l] & (((limb_t)1 << k) - 1);
+ }
+ }
+ if (bit2) {
+ add_one = 1;
+ } else {
+ /* round to even */
+ add_one = mpb_get_bit(r, shift);
+ }
+ } else {
+ add_one = 0;
+ }
+ break;
+ }
+
+ l = (unsigned)shift / LIMB_BITS;
+ shift = shift & (LIMB_BITS - 1);
+ if (l >= r->len) {
+ r->len = 1;
+ r->tab[0] = add_one;
+ } else {
+ if (l > 0) {
+ r->len -= l;
+ for(i = 0; i < r->len; i++)
+ r->tab[i] = r->tab[i + l];
+ }
+ if (shift != 0) {
+ mp_shr(r->tab, r->tab, r->len, shift, 0);
+ mpb_renorm(r);
+ }
+ if (add_one) {
+ limb_t a;
+ a = mp_add_ui(r->tab, 1, r->len);
+ if (a)
+ r->tab[r->len++] = a;
+ }
+ }
+ }
+}
+
+/* return -1, 0 or 1 */
+static int mpb_cmp(const mpb_t *a, const mpb_t *b)
+{
+ mp_size_t i;
+ if (a->len < b->len)
+ return -1;
+ else if (a->len > b->len)
+ return 1;
+ for(i = a->len - 1; i >= 0; i--) {
+ if (a->tab[i] != b->tab[i]) {
+ if (a->tab[i] < b->tab[i])
+ return -1;
+ else
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static void mpb_set_u64(mpb_t *r, uint64_t m)
+{
+#if LIMB_BITS == 64
+ r->tab[0] = m;
+ r->len = 1;
+#else
+ r->tab[0] = m;
+ r->tab[1] = m >> LIMB_BITS;
+ if (r->tab[1] == 0)
+ r->len = 1;
+ else
+ r->len = 2;
+#endif
+}
+
+static uint64_t mpb_get_u64(mpb_t *r)
+{
+#if LIMB_BITS == 64
+ return r->tab[0];
+#else
+ if (r->len == 1) {
+ return r->tab[0];
+ } else {
+ return r->tab[0] | ((uint64_t)r->tab[1] << LIMB_BITS);
+ }
+#endif
+}
+
+/* floor_log2() = position of the first non zero bit or -1 if zero. */
+static int mpb_floor_log2(mpb_t *a)
+{
+ limb_t v;
+ v = a->tab[a->len - 1];
+ if (v == 0)
+ return -1;
+ else
+ return a->len * LIMB_BITS - 1 - clz32(v);
+}
+
+#define MUL_LOG2_RADIX_BASE_LOG2 24
+
+/* round((1 << MUL_LOG2_RADIX_BASE_LOG2)/log2(i + 2)) */
+static const uint32_t mul_log2_radix_table[JS_RADIX_MAX - 1] = {
+ 0x000000, 0xa1849d, 0x000000, 0x6e40d2,
+ 0x6308c9, 0x5b3065, 0x000000, 0x50c24e,
+ 0x4d104d, 0x4a0027, 0x4768ce, 0x452e54,
+ 0x433d00, 0x418677, 0x000000, 0x3ea16b,
+ 0x3d645a, 0x3c43c2, 0x3b3b9a, 0x3a4899,
+ 0x39680b, 0x3897b3, 0x37d5af, 0x372069,
+ 0x367686, 0x35d6df, 0x354072, 0x34b261,
+ 0x342bea, 0x33ac62, 0x000000, 0x32bfd9,
+ 0x3251dd, 0x31e8d6, 0x318465,
+};
+
+/* return floor(a / log2(radix)) for -2048 <= a <= 2047 */
+static int mul_log2_radix(int a, int radix)
+{
+ int radix_bits, mult;
+
+ if ((radix & (radix - 1)) == 0) {
+ /* if the radix is a power of two better to do it exactly */
+ radix_bits = 31 - clz32(radix);
+ if (a < 0)
+ a -= radix_bits - 1;
+ return a / radix_bits;
+ } else {
+ mult = mul_log2_radix_table[radix - 2];
+ return ((int64_t)a * mult) >> MUL_LOG2_RADIX_BASE_LOG2;
+ }
+}
+
+#if 0
+static void build_mul_log2_radix_table(void)
+{
+ int base, radix, mult, col, base_log2;
+
+ base_log2 = 24;
+ base = 1 << base_log2;
+ col = 0;
+ for(radix = 2; radix <= 36; radix++) {
+ if ((radix & (radix - 1)) == 0)
+ mult = 0;
+ else
+ mult = lrint((double)base / log2(radix));
+ printf("0x%06x, ", mult);
+ if (++col == 4) {
+ printf("\n");
+ col = 0;
+ }
+ }
+ printf("\n");
+}
+
+static void mul_log2_radix_test(void)
+{
+ int radix, i, ref, r;
+
+ for(radix = 2; radix <= 36; radix++) {
+ for(i = -2048; i <= 2047; i++) {
+ ref = (int)floor((double)i / log2(radix));
+ r = mul_log2_radix(i, radix);
+ if (ref != r) {
+ printf("ERROR: radix=%d i=%d r=%d ref=%d\n",
+ radix, i, r, ref);
+ exit(1);
+ }
+ }
+ }
+ if (0)
+ build_mul_log2_radix_table();
+}
+#endif
+
+static void u32toa_len(char *buf, uint32_t n, size_t len)
+{
+ int digit, i;
+ for(i = len - 1; i >= 0; i--) {
+ digit = n % 10;
+ n = n / 10;
+ buf[i] = digit + '0';
+ }
+}
+
+/* for power of 2 radixes. len >= 1 */
+static void u64toa_bin_len(char *buf, uint64_t n, unsigned int radix_bits, int len)
+{
+ int digit, i;
+ unsigned int mask;
+
+ mask = (1 << radix_bits) - 1;
+ for(i = len - 1; i >= 0; i--) {
+ digit = n & mask;
+ n >>= radix_bits;
+ if (digit < 10)
+ digit += '0';
+ else
+ digit += 'a' - 10;
+ buf[i] = digit;
+ }
+}
+
+/* len >= 1. 2 <= radix <= 36 */
+static void limb_to_a(char *buf, limb_t n, unsigned int radix, int len)
+{
+ int digit, i;
+
+ if (radix == 10) {
+ /* specific case with constant divisor */
+#if LIMB_BITS == 32
+ u32toa_len(buf, n, len);
+#else
+ /* XXX: optimize */
+ for(i = len - 1; i >= 0; i--) {
+ digit = (limb_t)n % 10;
+ n = (limb_t)n / 10;
+ buf[i] = digit + '0';
+ }
+#endif
+ } else {
+ for(i = len - 1; i >= 0; i--) {
+ digit = (limb_t)n % radix;
+ n = (limb_t)n / radix;
+ if (digit < 10)
+ digit += '0';
+ else
+ digit += 'a' - 10;
+ buf[i] = digit;
+ }
+ }
+}
+
+size_t u32toa(char *buf, uint32_t n)
+{
+ char buf1[10], *q;
+ size_t len;
+
+ q = buf1 + sizeof(buf1);
+ do {
+ *--q = n % 10 + '0';
+ n /= 10;
+ } while (n != 0);
+ len = buf1 + sizeof(buf1) - q;
+ memcpy(buf, q, len);
+ return len;
+}
+
+size_t i32toa(char *buf, int32_t n)
+{
+ if (n >= 0) {
+ return u32toa(buf, n);
+ } else {
+ buf[0] = '-';
+ return u32toa(buf + 1, -(uint32_t)n) + 1;
+ }
+}
+
+#ifdef USE_FAST_INT
+size_t u64toa(char *buf, uint64_t n)
+{
+ if (n < 0x100000000) {
+ return u32toa(buf, n);
+ } else {
+ uint64_t n1;
+ char *q = buf;
+ uint32_t n2;
+
+ n1 = n / 1000000000;
+ n %= 1000000000;
+ if (n1 >= 0x100000000) {
+ n2 = n1 / 1000000000;
+ n1 = n1 % 1000000000;
+ /* at most two digits */
+ if (n2 >= 10) {
+ *q++ = n2 / 10 + '0';
+ n2 %= 10;
+ }
+ *q++ = n2 + '0';
+ u32toa_len(q, n1, 9);
+ q += 9;
+ } else {
+ q += u32toa(q, n1);
+ }
+ u32toa_len(q, n, 9);
+ q += 9;
+ return q - buf;
+ }
+}
+
+size_t i64toa(char *buf, int64_t n)
+{
+ if (n >= 0) {
+ return u64toa(buf, n);
+ } else {
+ buf[0] = '-';
+ return u64toa(buf + 1, -(uint64_t)n) + 1;
+ }
+}
+
+/* XXX: only tested for 1 <= n < 2^53 */
+size_t u64toa_radix(char *buf, uint64_t n, unsigned int radix)
+{
+ int radix_bits, l;
+ if (likely(radix == 10))
+ return u64toa(buf, n);
+ if ((radix & (radix - 1)) == 0) {
+ radix_bits = 31 - clz32(radix);
+ if (n == 0)
+ l = 1;
+ else
+ l = (64 - clz64(n) + radix_bits - 1) / radix_bits;
+ u64toa_bin_len(buf, n, radix_bits, l);
+ return l;
+ } else {
+ char buf1[41], *q; /* maximum length for radix = 3 */
+ size_t len;
+ int digit;
+ q = buf1 + sizeof(buf1);
+ do {
+ digit = n % radix;
+ n /= radix;
+ if (digit < 10)
+ digit += '0';
+ else
+ digit += 'a' - 10;
+ *--q = digit;
+ } while (n != 0);
+ len = buf1 + sizeof(buf1) - q;
+ memcpy(buf, q, len);
+ return len;
+ }
+}
+
+size_t i64toa_radix(char *buf, int64_t n, unsigned int radix)
+{
+ if (n >= 0) {
+ return u64toa_radix(buf, n, radix);
+ } else {
+ buf[0] = '-';
+ return u64toa_radix(buf + 1, -(uint64_t)n, radix) + 1;
+ }
+}
+#endif /* USE_FAST_INT */
+
+static const uint8_t digits_per_limb_table[JS_RADIX_MAX - 1] = {
+#if LIMB_BITS == 32
+32,20,16,13,12,11,10,10, 9, 9, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+#else
+64,40,32,27,24,22,21,20,19,18,17,17,16,16,16,15,15,15,14,14,14,14,13,13,13,13,13,13,13,12,12,12,12,12,12,
+#endif
+};
+
+static const uint32_t radix_base_table[JS_RADIX_MAX - 1] = {
+ 0x00000000, 0xcfd41b91, 0x00000000, 0x48c27395,
+ 0x81bf1000, 0x75db9c97, 0x40000000, 0xcfd41b91,
+ 0x3b9aca00, 0x8c8b6d2b, 0x19a10000, 0x309f1021,
+ 0x57f6c100, 0x98c29b81, 0x00000000, 0x18754571,
+ 0x247dbc80, 0x3547667b, 0x4c4b4000, 0x6b5a6e1d,
+ 0x94ace180, 0xcaf18367, 0x0b640000, 0x0e8d4a51,
+ 0x1269ae40, 0x17179149, 0x1cb91000, 0x23744899,
+ 0x2b73a840, 0x34e63b41, 0x40000000, 0x4cfa3cc1,
+ 0x5c13d840, 0x6d91b519, 0x81bf1000,
+};
+
+/* XXX: remove the table ? */
+static uint8_t dtoa_max_digits_table[JS_RADIX_MAX - 1] = {
+ 54, 35, 28, 24, 22, 20, 19, 18, 17, 17, 16, 16, 15, 15, 15, 14, 14, 14, 14, 14, 13, 13, 13, 13, 13, 13, 13, 12, 12, 12, 12, 12, 12, 12, 12,
+};
+
+/* we limit the maximum number of significant digits for atod to about
+ 128 bits of precision for non power of two bases. The only
+ requirement for Javascript is at least 20 digits in base 10. For
+ power of two bases, we do an exact rounding in all the cases. */
+static uint8_t atod_max_digits_table[JS_RADIX_MAX - 1] = {
+ 64, 80, 32, 55, 49, 45, 21, 40, 38, 37, 35, 34, 33, 32, 16, 31, 30, 30, 29, 29, 28, 28, 27, 27, 27, 26, 26, 26, 26, 25, 12, 25, 25, 24, 24,
+};
+
+/* if abs(d) >= B^max_exponent, it is an overflow */
+static const int16_t max_exponent[JS_RADIX_MAX - 1] = {
+ 1024, 647, 512, 442, 397, 365, 342, 324,
+ 309, 297, 286, 277, 269, 263, 256, 251,
+ 246, 242, 237, 234, 230, 227, 224, 221,
+ 218, 216, 214, 211, 209, 207, 205, 203,
+ 202, 200, 199,
+};
+
+/* if abs(d) <= B^min_exponent, it is an underflow */
+static const int16_t min_exponent[JS_RADIX_MAX - 1] = {
+-1075, -679, -538, -463, -416, -383, -359, -340,
+ -324, -311, -300, -291, -283, -276, -269, -263,
+ -258, -254, -249, -245, -242, -238, -235, -232,
+ -229, -227, -224, -222, -220, -217, -215, -214,
+ -212, -210, -208,
+};
+
+#if 0
+void build_tables(void)
+{
+ int r, j, radix, n, col, i;
+
+ /* radix_base_table */
+ for(radix = 2; radix <= 36; radix++) {
+ r = 1;
+ for(j = 0; j < digits_per_limb_table[radix - 2]; j++) {
+ r *= radix;
+ }
+ printf(" 0x%08x,", r);
+ if ((radix % 4) == 1)
+ printf("\n");
+ }
+ printf("\n");
+
+ /* dtoa_max_digits_table */
+ for(radix = 2; radix <= 36; radix++) {
+ /* Note: over estimated when the radix is a power of two */
+ printf(" %d,", 1 + (int)ceil(53.0 / log2(radix)));
+ }
+ printf("\n");
+
+ /* atod_max_digits_table */
+ for(radix = 2; radix <= 36; radix++) {
+ if ((radix & (radix - 1)) == 0) {
+ /* 64 bits is more than enough */
+ n = (int)floor(64.0 / log2(radix));
+ } else {
+ n = (int)floor(128.0 / log2(radix));
+ }
+ printf(" %d,", n);
+ }
+ printf("\n");
+
+ printf("static const int16_t max_exponent[JS_RADIX_MAX - 1] = {\n");
+ col = 0;
+ for(radix = 2; radix <= 36; radix++) {
+ printf("%5d, ", (int)ceil(1024 / log2(radix)));
+ if (++col == 8) {
+ col = 0;
+ printf("\n");
+ }
+ }
+ printf("\n};\n\n");
+
+ printf("static const int16_t min_exponent[JS_RADIX_MAX - 1] = {\n");
+ col = 0;
+ for(radix = 2; radix <= 36; radix++) {
+ printf("%5d, ", (int)floor(-1075 / log2(radix)));
+ if (++col == 8) {
+ col = 0;
+ printf("\n");
+ }
+ }
+ printf("\n};\n\n");
+
+ printf("static const uint32_t pow5_table[16] = {\n");
+ col = 0;
+ for(i = 2; i <= 17; i++) {
+ r = 1;
+ for(j = 0; j < i; j++) {
+ r *= 5;
+ }
+ printf("0x%08x, ", r);
+ if (++col == 4) {
+ col = 0;
+ printf("\n");
+ }
+ }
+ printf("\n};\n\n");
+
+ /* high part */
+ printf("static const uint8_t pow5h_table[4] = {\n");
+ col = 0;
+ for(i = 14; i <= 17; i++) {
+ uint64_t r1;
+ r1 = 1;
+ for(j = 0; j < i; j++) {
+ r1 *= 5;
+ }
+ printf("0x%08x, ", (uint32_t)(r1 >> 32));
+ if (++col == 4) {
+ col = 0;
+ printf("\n");
+ }
+ }
+ printf("\n};\n\n");
+}
+#endif
+
+/* n_digits >= 1. 0 <= dot_pos <= n_digits. If dot_pos == n_digits,
+ the dot is not displayed. 'a' is modified. */
+static int output_digits(char *buf,
+ mpb_t *a, int radix, int n_digits1,
+ int dot_pos)
+{
+ int n_digits, digits_per_limb, radix_bits, n, len;
+
+ n_digits = n_digits1;
+ if ((radix & (radix - 1)) == 0) {
+ /* radix = 2^radix_bits */
+ radix_bits = 31 - clz32(radix);
+ } else {
+ radix_bits = 0;
+ }
+ digits_per_limb = digits_per_limb_table[radix - 2];
+ if (radix_bits != 0) {
+ for(;;) {
+ n = min_int(n_digits, digits_per_limb);
+ n_digits -= n;
+ u64toa_bin_len(buf + n_digits, a->tab[0], radix_bits, n);
+ if (n_digits == 0)
+ break;
+ mpb_shr_round(a, digits_per_limb * radix_bits, JS_RNDZ);
+ }
+ } else {
+ limb_t r;
+ while (n_digits != 0) {
+ n = min_int(n_digits, digits_per_limb);
+ n_digits -= n;
+ r = mp_div1(a->tab, a->tab, a->len, radix_base_table[radix - 2], 0);
+ mpb_renorm(a);
+ limb_to_a(buf + n_digits, r, radix, n);
+ }
+ }
+
+ /* add the dot */
+ len = n_digits1;
+ if (dot_pos != n_digits1) {
+ memmove(buf + dot_pos + 1, buf + dot_pos, n_digits1 - dot_pos);
+ buf[dot_pos] = '.';
+ len++;
+ }
+ return len;
+}
+
+/* return (a, e_offset) such that a = a * (radix1*2^radix_shift)^f *
+ 2^-e_offset. 'f' can be negative. */
+static int mul_pow(mpb_t *a, int radix1, int radix_shift, int f, BOOL is_int, int e)
+{
+ int e_offset, d, n, n0;
+
+ e_offset = -f * radix_shift;
+ if (radix1 != 1) {
+ d = digits_per_limb_table[radix1 - 2];
+ if (f >= 0) {
+ limb_t h, b;
+
+ b = 0;
+ n0 = 0;
+ while (f != 0) {
+ n = min_int(f, d);
+ if (n != n0) {
+ b = pow_ui(radix1, n);
+ n0 = n;
+ }
+ h = mp_mul1(a->tab, a->tab, a->len, b, 0);
+ if (h != 0) {
+ a->tab[a->len++] = h;
+ }
+ f -= n;
+ }
+ } else {
+ int extra_bits, l, shift;
+ limb_t r, rem, b, b_inv;
+
+ f = -f;
+ l = (f + d - 1) / d; /* high bound for the number of limbs (XXX: make it better) */
+ e_offset += l * LIMB_BITS;
+ if (!is_int) {
+ /* at least 'e' bits are needed in the final result for rounding */
+ extra_bits = max_int(e - mpb_floor_log2(a), 0);
+ } else {
+ /* at least two extra bits are needed in the final result
+ for rounding */
+ extra_bits = max_int(2 + e - e_offset, 0);
+ }
+ e_offset += extra_bits;
+ mpb_shr_round(a, -(l * LIMB_BITS + extra_bits), JS_RNDZ);
+
+ b = 0;
+ b_inv = 0;
+ shift = 0;
+ n0 = 0;
+ rem = 0;
+ while (f != 0) {
+ n = min_int(f, d);
+ if (n != n0) {
+ b = pow_ui_inv(&b_inv, &shift, radix1, n);
+ n0 = n;
+ }
+ r = mp_div1norm(a->tab, a->tab, a->len, b, 0, b_inv, shift);
+ rem |= r;
+ mpb_renorm(a);
+ f -= n;
+ }
+ /* if the remainder is non zero, use it for rounding */
+ a->tab[0] |= (rem != 0);
+ }
+ }
+ return e_offset;
+}
+
+/* tmp1 = round(m*2^e*radix^f). 'tmp0' is a temporary storage */
+static void mul_pow_round(mpb_t *tmp1, uint64_t m, int e, int radix1, int radix_shift, int f,
+ int rnd_mode)
+{
+ int e_offset;
+
+ mpb_set_u64(tmp1, m);
+ e_offset = mul_pow(tmp1, radix1, radix_shift, f, TRUE, e);
+ mpb_shr_round(tmp1, -e + e_offset, rnd_mode);
+}
+
+/* return round(a*2^e_offset) rounded as a float64. 'a' is modified */
+static uint64_t round_to_d(int *pe, mpb_t *a, int e_offset, int rnd_mode)
+{
+ int e;
+ uint64_t m;
+
+ if (a->tab[0] == 0 && a->len == 1) {
+ /* zero result */
+ m = 0;
+ e = 0; /* don't care */
+ } else {
+ int prec, prec1, e_min;
+ e = mpb_floor_log2(a) + 1 - e_offset;
+ prec1 = 53;
+ e_min = -1021;
+ if (e < e_min) {
+ /* subnormal result or zero */
+ prec = prec1 - (e_min - e);
+ } else {
+ prec = prec1;
+ }
+ mpb_shr_round(a, e + e_offset - prec, rnd_mode);
+ m = mpb_get_u64(a);
+ m <<= (53 - prec);
+ /* mantissa overflow due to rounding */
+ if (m >= (uint64_t)1 << 53) {
+ m >>= 1;
+ e++;
+ }
+ }
+ *pe = e;
+ return m;
+}
+
+/* return (m, e) such that m*2^(e-53) = round(a * radix^f) with 2^52
+ <= m < 2^53 or m = 0.
+ 'a' is modified. */
+static uint64_t mul_pow_round_to_d(int *pe, mpb_t *a,
+ int radix1, int radix_shift, int f, int rnd_mode)
+{
+ int e_offset;
+
+ e_offset = mul_pow(a, radix1, radix_shift, f, FALSE, 55);
+ return round_to_d(pe, a, e_offset, rnd_mode);
+}
+
+#ifdef JS_DTOA_DUMP_STATS
+static int out_len_count[17];
+
+void js_dtoa_dump_stats(void)
+{
+ int i, sum;
+ sum = 0;
+ for(i = 0; i < 17; i++)
+ sum += out_len_count[i];
+ for(i = 0; i < 17; i++) {
+ printf("%2d %8d %5.2f%%\n",
+ i + 1, out_len_count[i], (double)out_len_count[i] / sum * 100);
+ }
+}
+#endif
+
+/* return a maximum bound of the string length. The bound depends on
+ 'd' only if format = JS_DTOA_FORMAT_FRAC or if JS_DTOA_EXP_DISABLED
+ is enabled. */
+int js_dtoa_max_len(double d, int radix, int n_digits, int flags)
+{
+ int fmt = flags & JS_DTOA_FORMAT_MASK;
+ int n, e;
+ uint64_t a;
+
+ if (fmt != JS_DTOA_FORMAT_FRAC) {
+ if (fmt == JS_DTOA_FORMAT_FREE) {
+ n = dtoa_max_digits_table[radix - 2];
+ } else {
+ n = n_digits;
+ }
+ if ((flags & JS_DTOA_EXP_MASK) == JS_DTOA_EXP_DISABLED) {
+ /* no exponential */
+ a = float64_as_uint64(d);
+ e = (a >> 52) & 0x7ff;
+ if (e == 0x7ff) {
+ /* NaN, Infinity */
+ n = 0;
+ } else {
+ e -= 1023;
+ /* XXX: adjust */
+ n += 10 + abs(mul_log2_radix(e - 1, radix));
+ }
+ } else {
+ /* extra: sign, 1 dot and exponent "e-1000" */
+ n += 1 + 1 + 6;
+ }
+ } else {
+ a = float64_as_uint64(d);
+ e = (a >> 52) & 0x7ff;
+ if (e == 0x7ff) {
+ /* NaN, Infinity */
+ n = 0;
+ } else {
+ /* high bound for the integer part */
+ e -= 1023;
+ /* x < 2^(e + 1) */
+ if (e < 0) {
+ n = 1;
+ } else {
+ n = 2 + mul_log2_radix(e - 1, radix);
+ }
+ /* sign, extra digit, 1 dot */
+ n += 1 + 1 + 1 + n_digits;
+ }
+ }
+ return max_int(n, 9); /* also include NaN and [-]Infinity */
+}
+
+#if defined(__SANITIZE_ADDRESS__) && 0
+static void *dtoa_malloc(uint64_t **pptr, size_t size)
+{
+ return malloc(size);
+}
+static void dtoa_free(void *ptr)
+{
+ free(ptr);
+}
+#else
+static void *dtoa_malloc(uint64_t **pptr, size_t size)
+{
+ void *ret;
+ ret = *pptr;
+ *pptr += (size + 7) / 8;
+ return ret;
+}
+
+static void dtoa_free(void *ptr)
+{
+}
+#endif
+
+/* return the length */
+int js_dtoa(char *buf, double d, int radix, int n_digits, int flags,
+ JSDTOATempMem *tmp_mem)
+{
+ uint64_t a, m, *mptr = tmp_mem->mem;
+ int e, sgn, l, E, P, i, E_max, radix1, radix_shift;
+ char *q;
+ mpb_t *tmp1, *mant_max;
+ int fmt = flags & JS_DTOA_FORMAT_MASK;
+
+ tmp1 = dtoa_malloc(&mptr, sizeof(mpb_t) + sizeof(limb_t) * DBIGNUM_LEN_MAX);
+ mant_max = dtoa_malloc(&mptr, sizeof(mpb_t) + sizeof(limb_t) * MANT_LEN_MAX);
+ assert((mptr - tmp_mem->mem) <= sizeof(JSDTOATempMem) / sizeof(mptr[0]));
+
+ radix_shift = ctz32(radix);
+ radix1 = radix >> radix_shift;
+ a = float64_as_uint64(d);
+ sgn = a >> 63;
+ e = (a >> 52) & 0x7ff;
+ m = a & (((uint64_t)1 << 52) - 1);
+ q = buf;
+ if (e == 0x7ff) {
+ if (m == 0) {
+ if (sgn)
+ *q++ = '-';
+ memcpy(q, "Infinity", 8);
+ q += 8;
+ } else {
+ memcpy(q, "NaN", 3);
+ q += 3;
+ }
+ goto done;
+ } else if (e == 0) {
+ if (m == 0) {
+ tmp1->len = 1;
+ tmp1->tab[0] = 0;
+ E = 1;
+ if (fmt == JS_DTOA_FORMAT_FREE)
+ P = 1;
+ else if (fmt == JS_DTOA_FORMAT_FRAC)
+ P = n_digits + 1;
+ else
+ P = n_digits;
+ goto output;
+ }
+ /* denormal number: convert to a normal number */
+ l = clz64(m) - 11;
+ e -= l - 1;
+ m <<= l;
+ } else {
+ m |= (uint64_t)1 << 52;
+ }
+ /* remove the bias */
+ e -= 1022;
+ /* d = 2^(e-53)*m */
+ // printf("m=0x%016" PRIx64 " e=%d\n", m, e);
+#ifdef USE_FAST_INT
+ if (fmt == JS_DTOA_FORMAT_FREE &&
+ e >= 1 && e <= 53 &&
+ (m & (((uint64_t)1 << (53 - e)) - 1)) == 0 &&
+ (flags & JS_DTOA_EXP_MASK) != JS_DTOA_EXP_ENABLED) {
+ m >>= 53 - e;
+ /* 'm' is never zero */
+ if (sgn)
+ *q++ = '-';
+ q += u64toa_radix(q, m, radix);
+ goto done;
+ }
+#endif
+
+ /* this choice of E implies F=round(x*B^(P-E) is such as:
+ B^(P-1) <= F < 2.B^P. */
+ E = 1 + mul_log2_radix(e - 1, radix);
+
+ if (fmt == JS_DTOA_FORMAT_FREE) {
+ int P_max, E0, e1, E_found, P_found;
+ uint64_t m1, mant_found, mant, mant_max1;
+ /* P_max is guarranteed to work by construction */
+ P_max = dtoa_max_digits_table[radix - 2];
+ E0 = E;
+ E_found = 0;
+ P_found = 0;
+ mant_found = 0;
+ /* find the minimum number of digits by successive tries */
+ P = P_max; /* P_max is guarateed to work */
+ for(;;) {
+ /* mant_max always fits on 64 bits */
+ mant_max1 = pow_ui(radix, P);
+ /* compute the mantissa in base B */
+ E = E0;
+ for(;;) {
+ /* XXX: add inexact flag */
+ mul_pow_round(tmp1, m, e - 53, radix1, radix_shift, P - E, JS_RNDN);
+ mant = mpb_get_u64(tmp1);
+ if (mant < mant_max1)
+ break;
+ E++; /* at most one iteration is possible */
+ }
+ /* remove useless trailing zero digits */
+ while ((mant % radix) == 0) {
+ mant /= radix;
+ P--;
+ }
+ /* garanteed to work for P = P_max */
+ if (P_found == 0)
+ goto prec_found;
+ /* convert back to base 2 */
+ mpb_set_u64(tmp1, mant);
+ m1 = mul_pow_round_to_d(&e1, tmp1, radix1, radix_shift, E - P, JS_RNDN);
+ // printf("P=%2d: m=0x%016" PRIx64 " e=%d m1=0x%016" PRIx64 " e1=%d\n", P, m, e, m1, e1);
+ /* Note: (m, e) is never zero here, so the exponent for m1
+ = 0 does not matter */
+ if (m1 == m && e1 == e) {
+ prec_found:
+ P_found = P;
+ E_found = E;
+ mant_found = mant;
+ if (P == 1)
+ break;
+ P--; /* try lower exponent */
+ } else {
+ break;
+ }
+ }
+ P = P_found;
+ E = E_found;
+ mpb_set_u64(tmp1, mant_found);
+#ifdef JS_DTOA_DUMP_STATS
+ if (radix == 10) {
+ out_len_count[P - 1]++;
+ }
+#endif
+ } else if (fmt == JS_DTOA_FORMAT_FRAC) {
+ int len;
+
+ assert(n_digits >= 0 && n_digits <= JS_DTOA_MAX_DIGITS);
+ /* P = max_int(E, 1) + n_digits; */
+ /* frac is rounded using RNDNA */
+ mul_pow_round(tmp1, m, e - 53, radix1, radix_shift, n_digits, JS_RNDNA);
+
+ /* "-0" is displayed as "0" */
+ if (sgn && !(tmp1->tab[0] == 0 && tmp1->len == 1)) {
+ *q++ = '-';
+ }
+ /* we add one extra digit on the left and remove it if needed
+ to avoid testing if the result is < radix^P */
+ len = output_digits(q, tmp1, radix, max_int(E + 1, 1) + n_digits,
+ max_int(E + 1, 1));
+ if (q[0] == '0' && len >= 2 && q[1] != '.') {
+ len--;
+ memmove(q, q + 1, len);
+ }
+ q += len;
+ goto done;
+ } else {
+ int pow_shift;
+ assert(n_digits >= 1 && n_digits <= JS_DTOA_MAX_DIGITS);
+ P = n_digits;
+ /* mant_max = radix^P */
+ mant_max->len = 1;
+ mant_max->tab[0] = 1;
+ pow_shift = mul_pow(mant_max, radix1, radix_shift, P, FALSE, 0);
+ mpb_shr_round(mant_max, pow_shift, JS_RNDZ);
+
+ for(;;) {
+ /* fixed and frac are rounded using RNDNA */
+ mul_pow_round(tmp1, m, e - 53, radix1, radix_shift, P - E, JS_RNDNA);
+ if (mpb_cmp(tmp1, mant_max) < 0)
+ break;
+ E++; /* at most one iteration is possible */
+ }
+ }
+ output:
+ /* "-0" is displayed as "0" if JS_DTOA_MINUS_ZERO is not present */
+ if (sgn && ((flags & JS_DTOA_MINUS_ZERO) ||
+ !(tmp1->tab[0] == 0 && tmp1->len == 1))) {
+ *q++ = '-';
+ }
+ if (fmt == JS_DTOA_FORMAT_FIXED)
+ E_max = n_digits;
+ else
+ E_max = dtoa_max_digits_table[radix - 2] + 4;
+ if ((flags & JS_DTOA_EXP_MASK) == JS_DTOA_EXP_ENABLED ||
+ ((flags & JS_DTOA_EXP_MASK) == JS_DTOA_EXP_AUTO && (E <= -6 || E > E_max))) {
+ q += output_digits(q, tmp1, radix, P, 1);
+ E--;
+ if (radix == 10) {
+ *q++ = 'e';
+ } else if (radix1 == 1 && radix_shift <= 4) {
+ E *= radix_shift;
+ *q++ = 'p';
+ } else {
+ *q++ = '@';
+ }
+ if (E < 0) {
+ *q++ = '-';
+ E = -E;
+ } else {
+ *q++ = '+';
+ }
+ q += u32toa(q, E);
+ } else if (E <= 0) {
+ *q++ = '0';
+ *q++ = '.';
+ for(i = 0; i < -E; i++)
+ *q++ = '0';
+ q += output_digits(q, tmp1, radix, P, P);
+ } else {
+ q += output_digits(q, tmp1, radix, P, min_int(P, E));
+ for(i = 0; i < E - P; i++)
+ *q++ = '0';
+ }
+ done:
+ *q = '\0';
+ dtoa_free(mant_max);
+ dtoa_free(tmp1);
+ return q - buf;
+}
+
+static inline int to_digit(int c)
+{
+ if (c >= '0' && c <= '9')
+ return c - '0';
+ else if (c >= 'A' && c <= 'Z')
+ return c - 'A' + 10;
+ else if (c >= 'a' && c <= 'z')
+ return c - 'a' + 10;
+ else
+ return 36;
+}
+
+/* r = r * radix_base + a. radix_base = 0 means radix_base = 2^32 */
+static void mpb_mul1_base(mpb_t *r, limb_t radix_base, limb_t a)
+{
+ int i;
+ if (r->tab[0] == 0 && r->len == 1) {
+ r->tab[0] = a;
+ } else {
+ if (radix_base == 0) {
+ for(i = r->len; i >= 0; i--) {
+ r->tab[i + 1] = r->tab[i];
+ }
+ r->tab[0] = a;
+ } else {
+ r->tab[r->len] = mp_mul1(r->tab, r->tab, r->len,
+ radix_base, a);
+ }
+ r->len++;
+ mpb_renorm(r);
+ }
+}
+
+/* XXX: add fast path for small integers */
+double js_atod(const char *str, const char **pnext, int radix, int flags,
+ JSATODTempMem *tmp_mem)
+{
+ uint64_t *mptr = tmp_mem->mem;
+ const char *p, *p_start;
+ limb_t cur_limb, radix_base, extra_digits;
+ int is_neg, digit_count, limb_digit_count, digits_per_limb, sep, radix1, radix_shift;
+ int radix_bits, expn, e, max_digits, expn_offset, dot_pos, sig_pos, pos;
+ mpb_t *tmp0;
+ double dval;
+ BOOL is_bin_exp, is_zero, expn_overflow;
+ uint64_t m, a;
+
+ tmp0 = dtoa_malloc(&mptr, sizeof(mpb_t) + sizeof(limb_t) * DBIGNUM_LEN_MAX);
+ assert((mptr - tmp_mem->mem) <= sizeof(JSATODTempMem) / sizeof(mptr[0]));
+ /* optional separator between digits */
+ sep = (flags & JS_ATOD_ACCEPT_UNDERSCORES) ? '_' : 256;
+
+ p = str;
+ is_neg = 0;
+ if (p[0] == '+') {
+ p++;
+ p_start = p;
+ } else if (p[0] == '-') {
+ is_neg = 1;
+ p++;
+ p_start = p;
+ } else {
+ p_start = p;
+ }
+
+ if (p[0] == '0') {
+ if ((p[1] == 'x' || p[1] == 'X') &&
+ (radix == 0 || radix == 16)) {
+ p += 2;
+ radix = 16;
+ } else if ((p[1] == 'o' || p[1] == 'O') &&
+ radix == 0 && (flags & JS_ATOD_ACCEPT_BIN_OCT)) {
+ p += 2;
+ radix = 8;
+ } else if ((p[1] == 'b' || p[1] == 'B') &&
+ radix == 0 && (flags & JS_ATOD_ACCEPT_BIN_OCT)) {
+ p += 2;
+ radix = 2;
+ } else if ((p[1] >= '0' && p[1] <= '9') &&
+ radix == 0 && (flags & JS_ATOD_ACCEPT_LEGACY_OCTAL)) {
+ int i;
+ sep = 256;
+ for (i = 1; (p[i] >= '0' && p[i] <= '7'); i++)
+ continue;
+ if (p[i] == '8' || p[i] == '9')
+ goto no_prefix;
+ p += 1;
+ radix = 8;
+ } else {
+ goto no_prefix;
+ }
+ /* there must be a digit after the prefix */
+ if (to_digit((uint8_t)*p) >= radix)
+ goto fail;
+ no_prefix: ;
+ } else {
+ if (!(flags & JS_ATOD_INT_ONLY) && strstart(p, "Infinity", &p))
+ goto overflow;
+ }
+ if (radix == 0)
+ radix = 10;
+
+ cur_limb = 0;
+ expn_offset = 0;
+ digit_count = 0;
+ limb_digit_count = 0;
+ max_digits = atod_max_digits_table[radix - 2];
+ digits_per_limb = digits_per_limb_table[radix - 2];
+ radix_base = radix_base_table[radix - 2];
+ radix_shift = ctz32(radix);
+ radix1 = radix >> radix_shift;
+ if (radix1 == 1) {
+ /* radix = 2^radix_bits */
+ radix_bits = radix_shift;
+ } else {
+ radix_bits = 0;
+ }
+ tmp0->len = 1;
+ tmp0->tab[0] = 0;
+ extra_digits = 0;
+ pos = 0;
+ dot_pos = -1;
+ /* skip leading zeros */
+ for(;;) {
+ if (*p == '.' && (p > p_start || to_digit(p[1]) < radix) &&
+ !(flags & JS_ATOD_INT_ONLY)) {
+ if (*p == sep)
+ goto fail;
+ if (dot_pos >= 0)
+ break;
+ dot_pos = pos;
+ p++;
+ }
+ if (*p == sep && p > p_start && p[1] == '0')
+ p++;
+ if (*p != '0')
+ break;
+ p++;
+ pos++;
+ }
+
+ sig_pos = pos;
+ for(;;) {
+ limb_t c;
+ if (*p == '.' && (p > p_start || to_digit(p[1]) < radix) &&
+ !(flags & JS_ATOD_INT_ONLY)) {
+ if (*p == sep)
+ goto fail;
+ if (dot_pos >= 0)
+ break;
+ dot_pos = pos;
+ p++;
+ }
+ if (*p == sep && p > p_start && to_digit(p[1]) < radix)
+ p++;
+ c = to_digit(*p);
+ if (c >= radix)
+ break;
+ p++;
+ pos++;
+ if (digit_count < max_digits) {
+ /* XXX: could be faster when radix_bits != 0 */
+ cur_limb = cur_limb * radix + c;
+ limb_digit_count++;
+ if (limb_digit_count == digits_per_limb) {
+ mpb_mul1_base(tmp0, radix_base, cur_limb);
+ cur_limb = 0;
+ limb_digit_count = 0;
+ }
+ digit_count++;
+ } else {
+ extra_digits |= c;
+ }
+ }
+ if (limb_digit_count != 0) {
+ mpb_mul1_base(tmp0, pow_ui(radix, limb_digit_count), cur_limb);
+ }
+ if (digit_count == 0) {
+ is_zero = TRUE;
+ expn_offset = 0;
+ } else {
+ is_zero = FALSE;
+ if (dot_pos < 0)
+ dot_pos = pos;
+ expn_offset = sig_pos + digit_count - dot_pos;
+ }
+
+ /* Use the extra digits for rounding if the base is a power of
+ two. Otherwise they are just truncated. */
+ if (radix_bits != 0 && extra_digits != 0) {
+ tmp0->tab[0] |= 1;
+ }
+
+ /* parse the exponent, if any */
+ expn = 0;
+ expn_overflow = FALSE;
+ is_bin_exp = FALSE;
+ if (!(flags & JS_ATOD_INT_ONLY) &&
+ ((radix == 10 && (*p == 'e' || *p == 'E')) ||
+ (radix != 10 && (*p == '@' ||
+ (radix_bits >= 1 && radix_bits <= 4 && (*p == 'p' || *p == 'P'))))) &&
+ p > p_start) {
+ BOOL exp_is_neg;
+ int c;
+ is_bin_exp = (*p == 'p' || *p == 'P');
+ p++;
+ exp_is_neg = 0;
+ if (*p == '+') {
+ p++;
+ } else if (*p == '-') {
+ exp_is_neg = 1;
+ p++;
+ }
+ c = to_digit(*p);
+ if (c >= 10)
+ goto fail; /* XXX: could stop before the exponent part */
+ expn = c;
+ p++;
+ for(;;) {
+ if (*p == sep && to_digit(p[1]) < 10)
+ p++;
+ c = to_digit(*p);
+ if (c >= 10)
+ break;
+ if (!expn_overflow) {
+ if (unlikely(expn > ((INT32_MAX - 2 - 9) / 10))) {
+ expn_overflow = TRUE;
+ } else {
+ expn = expn * 10 + c;
+ }
+ }
+ p++;
+ }
+ if (exp_is_neg)
+ expn = -expn;
+ /* if zero result, the exponent can be arbitrarily large */
+ if (!is_zero && expn_overflow) {
+ if (exp_is_neg)
+ a = 0;
+ else
+ a = (uint64_t)0x7ff << 52; /* infinity */
+ goto done;
+ }
+ }
+
+ if (p == p_start)
+ goto fail;
+
+ if (is_zero) {
+ a = 0;
+ } else {
+ int expn1;
+ if (radix_bits != 0) {
+ if (!is_bin_exp)
+ expn *= radix_bits;
+ expn -= expn_offset * radix_bits;
+ expn1 = expn + digit_count * radix_bits;
+ if (expn1 >= 1024 + radix_bits)
+ goto overflow;
+ else if (expn1 <= -1075)
+ goto underflow;
+ m = round_to_d(&e, tmp0, -expn, JS_RNDN);
+ } else {
+ expn -= expn_offset;
+ expn1 = expn + digit_count;
+ if (expn1 >= max_exponent[radix - 2] + 1)
+ goto overflow;
+ else if (expn1 <= min_exponent[radix - 2])
+ goto underflow;
+ m = mul_pow_round_to_d(&e, tmp0, radix1, radix_shift, expn, JS_RNDN);
+ }
+ if (m == 0) {
+ underflow:
+ a = 0;
+ } else if (e > 1024) {
+ overflow:
+ /* overflow */
+ a = (uint64_t)0x7ff << 52;
+ } else if (e < -1073) {
+ /* underflow */
+ /* XXX: check rounding */
+ a = 0;
+ } else if (e < -1021) {
+ /* subnormal */
+ a = m >> (-e - 1021);
+ } else {
+ a = ((uint64_t)(e + 1022) << 52) | (m & (((uint64_t)1 << 52) - 1));
+ }
+ }
+ done:
+ a |= (uint64_t)is_neg << 63;
+ dval = uint64_as_float64(a);
+ done1:
+ if (pnext)
+ *pnext = p;
+ dtoa_free(tmp0);
+ return dval;
+ fail:
+ dval = NAN;
+ goto done1;
+}
#include "quickjs.h"
#include "libregexp.h"
#include "libunicode.h"
+#include "dtoa.h"
#define OPTIMIZE 1
#define SHORT_OPCODES 1
/* 2 <= base <= 36 */
static char const digits[36] = "0123456789abcdefghijklmnopqrstuvwxyz";
-static char *u64toa(char *q, int64_t n, unsigned int base)
+/* special version going backwards */
+/* XXX: use dtoa.c */
+static char *js_u64toa(char *q, int64_t n, unsigned int base)
{
int digit;
if (base == 10) {
return q;
}
-static char *i64toa(char *buf_end, int64_t n, unsigned int base)
-{
- char *q = buf_end;
- int is_neg;
-
- is_neg = 0;
- if (n < 0) {
- is_neg = 1;
- n = -n;
- }
- *--q = '\0';
- q = u64toa(q, n, base);
- if (is_neg)
- *--q = '-';
- return q;
-}
-
/* len >= 1. 2 <= radix <= 36 */
static char *limb_to_a(char *q, js_limb_t n, unsigned int radix, int len)
{
static JSValue js_bigint_to_string1(JSContext *ctx, JSValueConst val, int radix)
{
if (JS_VALUE_GET_TAG(val) == JS_TAG_SHORT_BIG_INT) {
- char buf[66], *q;
-
- q = i64toa(buf + sizeof(buf), JS_VALUE_GET_SHORT_BIG_INT(val), radix);
- return JS_NewString(ctx, q);
+ char buf[66];
+ int len;
+ len = i64toa_radix(buf, JS_VALUE_GET_SHORT_BIG_INT(val), radix);
+ return js_new_string8(ctx, (const uint8_t *)buf, len);
} else {
JSBigInt *r, *tmp = NULL;
- char *buf, *q;
+ char *buf, *q, *buf_end;
int is_neg, n_bits, log2_radix, n_digits;
BOOL is_binary_radix;
JSValue res;
r = JS_VALUE_GET_PTR(val);
if (r->len == 1 && r->tab[0] == 0) {
/* '0' case */
- return JS_NewString(ctx, "0");
+ return js_new_string8(ctx, (const uint8_t *)"0", 1);
}
is_binary_radix = ((radix & (radix - 1)) == 0);
is_neg = js_bigint_sign(r);
}
q = buf + n_digits + is_neg + 1;
*--q = '\0';
+ buf_end = q;
if (!is_binary_radix) {
int len;
js_limb_t radix_base, v;
if (len == 1 && r->tab[0] < radix_base) {
v = r->tab[0];
if (v != 0) {
- q = u64toa(q, v, radix);
+ q = js_u64toa(q, v, radix);
}
break;
} else {
if (is_neg)
*--q = '-';
js_free(ctx, tmp);
- res = JS_NewString(ctx, q);
+ res = js_new_string8(ctx, (const uint8_t *)q, buf_end - q);
js_free(ctx, buf);
return res;
}
}
}
-/* XXX: remove */
-static double js_strtod(const char *str, int radix, BOOL is_float)
-{
- double d;
- int c;
-
- if (!is_float || radix != 10) {
- const char *p = str;
- uint64_t n_max, n;
- int int_exp, is_neg;
-
- is_neg = 0;
- if (*p == '-') {
- is_neg = 1;
- p++;
- }
-
- /* skip leading zeros */
- while (*p == '0')
- p++;
- n = 0;
- if (radix == 10)
- n_max = ((uint64_t)-1 - 9) / 10; /* most common case */
- else
- n_max = ((uint64_t)-1 - (radix - 1)) / radix;
- /* XXX: could be more precise */
- int_exp = 0;
- while (*p != '\0') {
- c = to_digit((uint8_t)*p);
- if (c >= radix)
- break;
- if (n <= n_max) {
- n = n * radix + c;
- } else {
- if (radix == 10)
- goto strtod_case;
- int_exp++;
- }
- p++;
- }
- d = n;
- if (int_exp != 0) {
- d *= pow(radix, int_exp);
- }
- if (is_neg)
- d = -d;
- } else {
- strtod_case:
- d = strtod(str, NULL);
- }
- return d;
-}
-
#define ATOD_INT_ONLY (1 << 0)
/* accept Oo and Ob prefixes in addition to 0x prefix if radix = 0 */
#define ATOD_ACCEPT_BIN_OCT (1 << 2)
/* return an exception in case of memory error. Return JS_NAN if
invalid syntax */
+/* XXX: directly use js_atod() */
static JSValue js_atof(JSContext *ctx, const char *str, const char **pp,
int radix, int flags)
{
int i, j, len;
BOOL buf_allocated = FALSE;
JSValue val;
-
+ JSATODTempMem atod_mem;
+
/* optional separator between digits */
sep = (flags & ATOD_ACCEPT_UNDERSCORES) ? '_' : 256;
has_legacy_octal = FALSE;
case ATOD_TYPE_FLOAT64:
{
double d;
- d = js_strtod(buf, radix, is_float);
+ d = js_atod(buf,NULL, radix, is_float ? 0 : JS_ATOD_INT_ONLY,
+ &atod_mem);
/* return int or float64 */
val = JS_NewFloat64(ctx, d);
}
return js_bigint_to_string1(ctx, val, 10);
}
-/* buf1 contains the printf result */
-static void js_ecvt1(double d, int n_digits, int *decpt, int *sign, char *buf,
- int rounding_mode, char *buf1, int buf1_size)
-{
- if (rounding_mode != FE_TONEAREST)
- fesetround(rounding_mode);
- snprintf(buf1, buf1_size, "%+.*e", n_digits - 1, d);
- if (rounding_mode != FE_TONEAREST)
- fesetround(FE_TONEAREST);
- *sign = (buf1[0] == '-');
- /* mantissa */
- buf[0] = buf1[1];
- if (n_digits > 1)
- memcpy(buf + 1, buf1 + 3, n_digits - 1);
- buf[n_digits] = '\0';
- /* exponent */
- *decpt = atoi(buf1 + n_digits + 2 + (n_digits > 1)) + 1;
-}
-
-/* maximum buffer size for js_dtoa */
-#define JS_DTOA_BUF_SIZE 128
-
-/* needed because ecvt usually limits the number of digits to
- 17. Return the number of digits. */
-static int js_ecvt(double d, int n_digits, int *decpt, int *sign, char *buf,
- BOOL is_fixed)
-{
- int rounding_mode;
- char buf_tmp[JS_DTOA_BUF_SIZE];
-
- if (!is_fixed) {
- unsigned int n_digits_min, n_digits_max;
- /* find the minimum amount of digits (XXX: inefficient but simple) */
- n_digits_min = 1;
- n_digits_max = 17;
- while (n_digits_min < n_digits_max) {
- n_digits = (n_digits_min + n_digits_max) / 2;
- js_ecvt1(d, n_digits, decpt, sign, buf, FE_TONEAREST,
- buf_tmp, sizeof(buf_tmp));
- if (strtod(buf_tmp, NULL) == d) {
- /* no need to keep the trailing zeros */
- while (n_digits >= 2 && buf[n_digits - 1] == '0')
- n_digits--;
- n_digits_max = n_digits;
- } else {
- n_digits_min = n_digits + 1;
- }
- }
- n_digits = n_digits_max;
- rounding_mode = FE_TONEAREST;
- } else {
- rounding_mode = FE_TONEAREST;
-#ifdef CONFIG_PRINTF_RNDN
- {
- char buf1[JS_DTOA_BUF_SIZE], buf2[JS_DTOA_BUF_SIZE];
- int decpt1, sign1, decpt2, sign2;
- /* The JS rounding is specified as round to nearest ties away
- from zero (RNDNA), but in printf the "ties" case is not
- specified (for example it is RNDN for glibc, RNDNA for
- Windows), so we must round manually. */
- js_ecvt1(d, n_digits + 1, &decpt1, &sign1, buf1, FE_TONEAREST,
- buf_tmp, sizeof(buf_tmp));
- /* XXX: could use 2 digits to reduce the average running time */
- if (buf1[n_digits] == '5') {
- js_ecvt1(d, n_digits + 1, &decpt1, &sign1, buf1, FE_DOWNWARD,
- buf_tmp, sizeof(buf_tmp));
- js_ecvt1(d, n_digits + 1, &decpt2, &sign2, buf2, FE_UPWARD,
- buf_tmp, sizeof(buf_tmp));
- if (memcmp(buf1, buf2, n_digits + 1) == 0 && decpt1 == decpt2) {
- /* exact result: round away from zero */
- if (sign1)
- rounding_mode = FE_DOWNWARD;
- else
- rounding_mode = FE_UPWARD;
- }
- }
- }
-#endif /* CONFIG_PRINTF_RNDN */
- }
- js_ecvt1(d, n_digits, decpt, sign, buf, rounding_mode,
- buf_tmp, sizeof(buf_tmp));
- return n_digits;
-}
-
-static int js_fcvt1(char (*buf)[JS_DTOA_BUF_SIZE], double d, int n_digits,
- int rounding_mode)
-{
- int n;
- if (rounding_mode != FE_TONEAREST)
- fesetround(rounding_mode);
- n = snprintf(*buf, sizeof(*buf), "%.*f", n_digits, d);
- if (rounding_mode != FE_TONEAREST)
- fesetround(FE_TONEAREST);
- assert(n < sizeof(*buf));
- return n;
-}
-
-static void js_fcvt(char (*buf)[JS_DTOA_BUF_SIZE], double d, int n_digits)
+static JSValue js_dtoa2(JSContext *ctx,
+ double d, int radix, int n_digits, int flags)
{
- int rounding_mode;
- rounding_mode = FE_TONEAREST;
-#ifdef CONFIG_PRINTF_RNDN
- {
- int n1, n2;
- char buf1[JS_DTOA_BUF_SIZE];
- char buf2[JS_DTOA_BUF_SIZE];
-
- /* The JS rounding is specified as round to nearest ties away from
- zero (RNDNA), but in printf the "ties" case is not specified
- (for example it is RNDN for glibc, RNDNA for Windows), so we
- must round manually. */
- n1 = js_fcvt1(&buf1, d, n_digits + 1, FE_TONEAREST);
- rounding_mode = FE_TONEAREST;
- /* XXX: could use 2 digits to reduce the average running time */
- if (buf1[n1 - 1] == '5') {
- n1 = js_fcvt1(&buf1, d, n_digits + 1, FE_DOWNWARD);
- n2 = js_fcvt1(&buf2, d, n_digits + 1, FE_UPWARD);
- if (n1 == n2 && memcmp(buf1, buf2, n1) == 0) {
- /* exact result: round away from zero */
- if (buf1[0] == '-')
- rounding_mode = FE_DOWNWARD;
- else
- rounding_mode = FE_UPWARD;
- }
- }
- }
-#endif /* CONFIG_PRINTF_RNDN */
- js_fcvt1(buf, d, n_digits, rounding_mode);
-}
-
-/* radix != 10 is only supported with flags = JS_DTOA_VAR_FORMAT */
-/* use as many digits as necessary */
-#define JS_DTOA_VAR_FORMAT (0 << 0)
-/* use n_digits significant digits (1 <= n_digits <= 101) */
-#define JS_DTOA_FIXED_FORMAT (1 << 0)
-/* force fractional format: [-]dd.dd with n_digits fractional digits */
-#define JS_DTOA_FRAC_FORMAT (2 << 0)
-/* force exponential notation either in fixed or variable format */
-#define JS_DTOA_FORCE_EXP (1 << 2)
-
-/* XXX: slow and maybe not fully correct. Use libbf when it is fast enough.
- XXX: radix != 10 is only supported for small integers
-*/
-static void js_dtoa1(char (*buf)[JS_DTOA_BUF_SIZE], double d,
- int radix, int n_digits, int flags)
-{
- char *q;
-
- if (!isfinite(d)) {
- if (isnan(d)) {
- pstrcpy(*buf, sizeof(*buf), "NaN");
- } else if (d < 0) {
- pstrcpy(*buf, sizeof(*buf), "-Infinity");
- } else {
- pstrcpy(*buf, sizeof(*buf), "Infinity");
- }
- } else if (flags == JS_DTOA_VAR_FORMAT) {
- int64_t i64;
- char buf1[70], *ptr;
- if (d > (double)MAX_SAFE_INTEGER || d < (double)-MAX_SAFE_INTEGER)
- goto generic_conv;
- i64 = (int64_t)d;
- if (d != i64)
- goto generic_conv;
- /* fast path for integers */
- ptr = i64toa(buf1 + sizeof(buf1), i64, radix);
- pstrcpy(*buf, sizeof(*buf), ptr);
- } else {
- if (d == 0.0)
- d = 0.0; /* convert -0 to 0 */
- if (flags == JS_DTOA_FRAC_FORMAT) {
- js_fcvt(buf, d, n_digits);
- } else {
- char buf1[JS_DTOA_BUF_SIZE];
- int sign, decpt, k, n, i, p, n_max;
- BOOL is_fixed;
- generic_conv:
- is_fixed = ((flags & 3) == JS_DTOA_FIXED_FORMAT);
- if (is_fixed) {
- n_max = n_digits;
- } else {
- n_max = 21;
- }
- /* the number has k digits (k >= 1) */
- k = js_ecvt(d, n_digits, &decpt, &sign, buf1, is_fixed);
- n = decpt; /* d=10^(n-k)*(buf1) i.e. d= < x.yyyy 10^(n-1) */
- q = *buf;
- if (sign)
- *q++ = '-';
- if (flags & JS_DTOA_FORCE_EXP)
- goto force_exp;
- if (n >= 1 && n <= n_max) {
- if (k <= n) {
- memcpy(q, buf1, k);
- q += k;
- for(i = 0; i < (n - k); i++)
- *q++ = '0';
- *q = '\0';
- } else {
- /* k > n */
- memcpy(q, buf1, n);
- q += n;
- *q++ = '.';
- for(i = 0; i < (k - n); i++)
- *q++ = buf1[n + i];
- *q = '\0';
- }
- } else if (n >= -5 && n <= 0) {
- *q++ = '0';
- *q++ = '.';
- for(i = 0; i < -n; i++)
- *q++ = '0';
- memcpy(q, buf1, k);
- q += k;
- *q = '\0';
- } else {
- force_exp:
- /* exponential notation */
- *q++ = buf1[0];
- if (k > 1) {
- *q++ = '.';
- for(i = 1; i < k; i++)
- *q++ = buf1[i];
- }
- *q++ = 'e';
- p = n - 1;
- if (p >= 0)
- *q++ = '+';
- snprintf(q, *buf + sizeof(*buf) - q, "%d", p);
- }
- }
- }
-}
-
-static JSValue js_dtoa(JSContext *ctx,
- double d, int radix, int n_digits, int flags)
-{
- char buf[JS_DTOA_BUF_SIZE];
- js_dtoa1(&buf, d, radix, n_digits, flags);
- return JS_NewString(ctx, buf);
-}
-
-static JSValue js_dtoa_radix(JSContext *ctx, double d, int radix)
-{
- char buf[2200], *ptr, *ptr2;
- /* d is finite */
- int sign = d < 0;
- int digit;
- double frac, d0;
- int64_t n0 = 0;
- d = fabs(d);
- d0 = trunc(d);
- frac = d - d0;
- ptr = buf + 1100;
- *ptr = '\0';
- if (d0 <= MAX_SAFE_INTEGER) {
- int64_t n = n0 = (int64_t)d0;
- while (n >= radix) {
- digit = n % radix;
- n = n / radix;
- *--ptr = digits[digit];
- }
- *--ptr = digits[(int)n];
- } else {
- /* no decimals */
- while (d0 >= radix) {
- digit = fmod(d0, radix);
- d0 = trunc(d0 / radix);
- if (d0 >= MAX_SAFE_INTEGER)
- digit = 0;
- *--ptr = digits[digit];
- }
- *--ptr = digits[(int)d0];
- goto done;
- }
- if (frac != 0) {
- double log2_radix = log2(radix);
- double prec = 1023 + 51; // handle subnormals
- ptr2 = buf + 1100;
- *ptr2++ = '.';
- while (frac != 0 && n0 <= MAX_SAFE_INTEGER/2 && prec > 0) {
- frac *= radix;
- digit = trunc(frac);
- frac -= digit;
- *ptr2++ = digits[digit];
- n0 = n0 * radix + digit;
- prec -= log2_radix;
- }
- *ptr2 = '\0';
- if (frac * radix >= radix / 2) {
- char nine = digits[radix - 1];
- // round to closest
- while (ptr2[-1] == nine)
- *--ptr2 = '\0';
- if (ptr2[-1] == '.') {
- *--ptr2 = '\0';
- while (ptr2[-1] == nine)
- *--ptr2 = '0';
- }
- if (ptr2 - 1 == ptr)
- *--ptr = '1';
- else
- ptr2[-1] += 1;
- } else {
- while (ptr2[-1] == '0')
- *--ptr2 = '\0';
- if (ptr2[-1] == '.')
- *--ptr2 = '\0';
- }
+ char static_buf[128], *buf, *tmp_buf;
+ int len, len_max;
+ JSValue res;
+ JSDTOATempMem dtoa_mem;
+ len_max = js_dtoa_max_len(d, radix, n_digits, flags);
+
+ /* longer buffer may be used if radix != 10 */
+ if (len_max > sizeof(static_buf) - 1) {
+ tmp_buf = js_malloc(ctx, len_max + 1);
+ if (!tmp_buf)
+ return JS_EXCEPTION;
+ buf = tmp_buf;
+ } else {
+ tmp_buf = NULL;
+ buf = static_buf;
}
-done:
- ptr[-1] = '-';
- ptr -= sign;
- return JS_NewString(ctx, ptr);
+ len = js_dtoa(buf, d, radix, n_digits, flags, &dtoa_mem);
+ res = js_new_string8(ctx, (const uint8_t *)buf, len);
+ js_free(ctx, tmp_buf);
+ return res;
}
JSValue JS_ToStringInternal(JSContext *ctx, JSValueConst val, BOOL is_ToPropertyKey)
case JS_TAG_STRING:
return JS_DupValue(ctx, val);
case JS_TAG_INT:
- snprintf(buf, sizeof(buf), "%d", JS_VALUE_GET_INT(val));
- str = buf;
- goto new_string;
+ {
+ int len;
+ len = i32toa(buf, JS_VALUE_GET_INT(val));
+ return js_new_string8(ctx, (const uint8_t *)buf, len);
+ }
+ break;
case JS_TAG_BOOL:
return JS_AtomToString(ctx, JS_VALUE_GET_BOOL(val) ?
JS_ATOM_true : JS_ATOM_false);
return JS_ThrowTypeError(ctx, "cannot convert symbol to string");
}
case JS_TAG_FLOAT64:
- return js_dtoa(ctx, JS_VALUE_GET_FLOAT64(val), 10, 0,
- JS_DTOA_VAR_FORMAT);
+ return js_dtoa2(ctx, JS_VALUE_GET_FLOAT64(val), 10, 0,
+ JS_DTOA_FORMAT_FREE);
case JS_TAG_SHORT_BIG_INT:
case JS_TAG_BIG_INT:
return js_bigint_to_string(ctx, val);
int argc, JSValueConst *argv, int magic)
{
JSValue val;
- int base;
+ int base, flags;
double d;
val = js_thisNumberValue(ctx, this_val);
goto fail;
}
if (JS_VALUE_GET_TAG(val) == JS_TAG_INT) {
- char buf1[70], *ptr;
- ptr = i64toa(buf1 + sizeof(buf1), JS_VALUE_GET_INT(val), base);
- return JS_NewString(ctx, ptr);
+ char buf1[70];
+ int len;
+ len = i64toa_radix(buf1, JS_VALUE_GET_INT(val), base);
+ return js_new_string8(ctx, (const uint8_t *)buf1, len);
}
if (JS_ToFloat64Free(ctx, &d, val))
return JS_EXCEPTION;
- if (base != 10 && isfinite(d)) {
- return js_dtoa_radix(ctx, d, base);
- }
- return js_dtoa(ctx, d, base, 0, JS_DTOA_VAR_FORMAT);
+ flags = JS_DTOA_FORMAT_FREE;
+ if (base != 10)
+ flags |= JS_DTOA_EXP_DISABLED;
+ return js_dtoa2(ctx, d, base, 0, flags);
fail:
JS_FreeValue(ctx, val);
return JS_EXCEPTION;
int argc, JSValueConst *argv)
{
JSValue val;
- int f;
+ int f, flags;
double d;
val = js_thisNumberValue(ctx, this_val);
return JS_EXCEPTION;
if (f < 0 || f > 100)
return JS_ThrowRangeError(ctx, "invalid number of digits");
- if (fabs(d) >= 1e21) {
- return JS_ToStringFree(ctx, __JS_NewFloat64(ctx, d));
- } else {
- return js_dtoa(ctx, d, 10, f, JS_DTOA_FRAC_FORMAT);
- }
+ if (fabs(d) >= 1e21)
+ flags = JS_DTOA_FORMAT_FREE;
+ else
+ flags = JS_DTOA_FORMAT_FRAC;
+ return js_dtoa2(ctx, d, 10, f, flags);
}
static JSValue js_number_toExponential(JSContext *ctx, JSValueConst this_val,
return JS_ToStringFree(ctx, __JS_NewFloat64(ctx, d));
}
if (JS_IsUndefined(argv[0])) {
- flags = 0;
+ flags = JS_DTOA_FORMAT_FREE;
f = 0;
} else {
if (f < 0 || f > 100)
return JS_ThrowRangeError(ctx, "invalid number of digits");
f++;
- flags = JS_DTOA_FIXED_FORMAT;
+ flags = JS_DTOA_FORMAT_FIXED;
}
- return js_dtoa(ctx, d, 10, f, flags | JS_DTOA_FORCE_EXP);
+ return js_dtoa2(ctx, d, 10, f, flags | JS_DTOA_EXP_ENABLED);
}
static JSValue js_number_toPrecision(JSContext *ctx, JSValueConst this_val,
}
if (p < 1 || p > 100)
return JS_ThrowRangeError(ctx, "invalid number of digits");
- return js_dtoa(ctx, d, 10, p, JS_DTOA_FIXED_FORMAT);
+ return js_dtoa2(ctx, d, 10, p, JS_DTOA_FORMAT_FIXED);
}
static const JSCFunctionListEntry js_number_proto_funcs[] = {