2281 lines
70 KiB
C
2281 lines
70 KiB
C
/*
|
|
* Number-to-string and string-to-number conversions.
|
|
*
|
|
* Slow path number-to-string and string-to-number conversion is based on
|
|
* a Dragon4 variant, with fast paths for small integers. Big integer
|
|
* arithmetic is needed for guaranteeing that the conversion is correct
|
|
* and uses a minimum number of digits. The big number arithmetic has a
|
|
* fixed maximum size and does not require dynamic allocations.
|
|
*
|
|
* See: doc/number-conversion.rst.
|
|
*/
|
|
|
|
#include "third_party/duktape/duk_internal.h"
|
|
|
|
#define DUK__IEEE_DOUBLE_EXP_BIAS 1023
|
|
#define DUK__IEEE_DOUBLE_EXP_MIN (-1022) /* biased exp == 0 -> denormal, exp -1022 */
|
|
|
|
#define DUK__DIGITCHAR(x) duk_lc_digits[(x)]
|
|
|
|
/*
|
|
* Tables generated with util/gennumdigits.py.
|
|
*
|
|
* duk__str2num_digits_for_radix indicates, for each radix, how many input
|
|
* digits should be considered significant for string-to-number conversion.
|
|
* The input is also padded to this many digits to give the Dragon4
|
|
* conversion enough (apparent) precision to work with.
|
|
*
|
|
* duk__str2num_exp_limits indicates, for each radix, the radix-specific
|
|
* minimum/maximum exponent values (for a Dragon4 integer mantissa)
|
|
* below and above which the number is guaranteed to underflow to zero
|
|
* or overflow to Infinity. This allows parsing to keep bigint values
|
|
* bounded.
|
|
*/
|
|
|
|
DUK_LOCAL const duk_uint8_t duk__str2num_digits_for_radix[] = {
|
|
69, 44, 35, 30, 27, 25, 23, 22, 20, 20, /* 2 to 11 */
|
|
20, 19, 19, 18, 18, 17, 17, 17, 16, 16, /* 12 to 21 */
|
|
16, 16, 16, 15, 15, 15, 15, 15, 15, 14, /* 22 to 31 */
|
|
14, 14, 14, 14, 14 /* 31 to 36 */
|
|
};
|
|
|
|
typedef struct {
|
|
duk_int16_t upper;
|
|
duk_int16_t lower;
|
|
} duk__exp_limits;
|
|
|
|
DUK_LOCAL const duk__exp_limits duk__str2num_exp_limits[] = {
|
|
{ 957, -1147 }, { 605, -725 }, { 479, -575 }, { 414, -496 },
|
|
{ 372, -446 }, { 342, -411 }, { 321, -384 }, { 304, -364 },
|
|
{ 291, -346 }, { 279, -334 }, { 268, -323 }, { 260, -312 },
|
|
{ 252, -304 }, { 247, -296 }, { 240, -289 }, { 236, -283 },
|
|
{ 231, -278 }, { 227, -273 }, { 223, -267 }, { 220, -263 },
|
|
{ 216, -260 }, { 213, -256 }, { 210, -253 }, { 208, -249 },
|
|
{ 205, -246 }, { 203, -244 }, { 201, -241 }, { 198, -239 },
|
|
{ 196, -237 }, { 195, -234 }, { 193, -232 }, { 191, -230 },
|
|
{ 190, -228 }, { 188, -226 }, { 187, -225 },
|
|
};
|
|
|
|
/*
|
|
* Limited functionality bigint implementation.
|
|
*
|
|
* Restricted to non-negative numbers with less than 32 * DUK__BI_MAX_PARTS bits,
|
|
* with the caller responsible for ensuring this is never exceeded. No memory
|
|
* allocation (except stack) is needed for bigint computation. Operations
|
|
* have been tailored for number conversion needs.
|
|
*
|
|
* Argument order is "assignment order", i.e. target first, then arguments:
|
|
* x <- y * z --> duk__bi_mul(x, y, z);
|
|
*/
|
|
|
|
/* This upper value has been experimentally determined; debug build will check
|
|
* bigint size with assertions.
|
|
*/
|
|
#define DUK__BI_MAX_PARTS 37 /* 37x32 = 1184 bits */
|
|
|
|
#if defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 2)
|
|
#define DUK__BI_PRINT(name,x) duk__bi_print((name),(x))
|
|
#else
|
|
#define DUK__BI_PRINT(name,x)
|
|
#endif
|
|
|
|
/* Current size is about 152 bytes. */
|
|
typedef struct {
|
|
duk_small_int_t n;
|
|
duk_uint32_t v[DUK__BI_MAX_PARTS]; /* low to high */
|
|
} duk__bigint;
|
|
|
|
#if defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 2)
|
|
DUK_LOCAL void duk__bi_print(const char *name, duk__bigint *x) {
|
|
/* Overestimate required size; debug code so not critical to be tight. */
|
|
char buf[DUK__BI_MAX_PARTS * 9 + 64];
|
|
char *p = buf;
|
|
duk_small_int_t i;
|
|
|
|
/* No NUL term checks in this debug code. */
|
|
p += DUK_SPRINTF(p, "%p n=%ld", (void *) x, (long) x->n);
|
|
if (x->n == 0) {
|
|
p += DUK_SPRINTF(p, " 0");
|
|
}
|
|
for (i = x->n - 1; i >= 0; i--) {
|
|
p += DUK_SPRINTF(p, " %08lx", (unsigned long) x->v[i]);
|
|
}
|
|
|
|
DUK_DDD(DUK_DDDPRINT("%s: %s", (const char *) name, (const char *) buf));
|
|
}
|
|
#endif
|
|
|
|
#if defined(DUK_USE_ASSERTIONS)
|
|
DUK_LOCAL duk_small_int_t duk__bi_is_valid(duk__bigint *x) {
|
|
return (duk_small_int_t)
|
|
( ((x->n >= 0) && (x->n <= DUK__BI_MAX_PARTS)) /* is valid size */ &&
|
|
((x->n == 0) || (x->v[x->n - 1] != 0)) /* is normalized */ );
|
|
}
|
|
#endif
|
|
|
|
DUK_LOCAL void duk__bi_normalize(duk__bigint *x) {
|
|
duk_small_int_t i;
|
|
|
|
for (i = x->n - 1; i >= 0; i--) {
|
|
if (x->v[i] != 0) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Note: if 'x' is zero, x->n becomes 0 here */
|
|
x->n = i + 1;
|
|
DUK_ASSERT(duk__bi_is_valid(x));
|
|
}
|
|
|
|
/* x <- y */
|
|
DUK_LOCAL void duk__bi_copy(duk__bigint *x, duk__bigint *y) {
|
|
duk_small_int_t n;
|
|
|
|
n = y->n;
|
|
x->n = n;
|
|
/* No need to special case n == 0. */
|
|
duk_memcpy((void *) x->v, (const void *) y->v, (size_t) (sizeof(duk_uint32_t) * (size_t) n));
|
|
}
|
|
|
|
DUK_LOCAL void duk__bi_set_small(duk__bigint *x, duk_uint32_t v) {
|
|
if (v == 0U) {
|
|
x->n = 0;
|
|
} else {
|
|
x->n = 1;
|
|
x->v[0] = v;
|
|
}
|
|
DUK_ASSERT(duk__bi_is_valid(x));
|
|
}
|
|
|
|
/* Return value: <0 <=> x < y
|
|
* 0 <=> x == y
|
|
* >0 <=> x > y
|
|
*/
|
|
DUK_LOCAL int duk__bi_compare(duk__bigint *x, duk__bigint *y) {
|
|
duk_small_int_t i, nx, ny;
|
|
duk_uint32_t tx, ty;
|
|
|
|
DUK_ASSERT(duk__bi_is_valid(x));
|
|
DUK_ASSERT(duk__bi_is_valid(y));
|
|
|
|
nx = x->n;
|
|
ny = y->n;
|
|
if (nx > ny) {
|
|
goto ret_gt;
|
|
}
|
|
if (nx < ny) {
|
|
goto ret_lt;
|
|
}
|
|
for (i = nx - 1; i >= 0; i--) {
|
|
tx = x->v[i];
|
|
ty = y->v[i];
|
|
|
|
if (tx > ty) {
|
|
goto ret_gt;
|
|
}
|
|
if (tx < ty) {
|
|
goto ret_lt;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
|
|
ret_gt:
|
|
return 1;
|
|
|
|
ret_lt:
|
|
return -1;
|
|
}
|
|
|
|
/* x <- y + z */
|
|
#if defined(DUK_USE_64BIT_OPS)
|
|
DUK_LOCAL void duk__bi_add(duk__bigint *x, duk__bigint *y, duk__bigint *z) {
|
|
duk_uint64_t tmp;
|
|
duk_small_int_t i, ny, nz;
|
|
|
|
DUK_ASSERT(duk__bi_is_valid(y));
|
|
DUK_ASSERT(duk__bi_is_valid(z));
|
|
|
|
if (z->n > y->n) {
|
|
duk__bigint *t;
|
|
t = y; y = z; z = t;
|
|
}
|
|
DUK_ASSERT(y->n >= z->n);
|
|
|
|
ny = y->n; nz = z->n;
|
|
tmp = 0U;
|
|
for (i = 0; i < ny; i++) {
|
|
DUK_ASSERT(i < DUK__BI_MAX_PARTS);
|
|
tmp += y->v[i];
|
|
if (i < nz) {
|
|
tmp += z->v[i];
|
|
}
|
|
x->v[i] = (duk_uint32_t) (tmp & 0xffffffffUL);
|
|
tmp = tmp >> 32;
|
|
}
|
|
if (tmp != 0U) {
|
|
DUK_ASSERT(i < DUK__BI_MAX_PARTS);
|
|
x->v[i++] = (duk_uint32_t) tmp;
|
|
}
|
|
x->n = i;
|
|
DUK_ASSERT(x->n <= DUK__BI_MAX_PARTS);
|
|
|
|
/* no need to normalize */
|
|
DUK_ASSERT(duk__bi_is_valid(x));
|
|
}
|
|
#else /* DUK_USE_64BIT_OPS */
|
|
DUK_LOCAL void duk__bi_add(duk__bigint *x, duk__bigint *y, duk__bigint *z) {
|
|
duk_uint32_t carry, tmp1, tmp2;
|
|
duk_small_int_t i, ny, nz;
|
|
|
|
DUK_ASSERT(duk__bi_is_valid(y));
|
|
DUK_ASSERT(duk__bi_is_valid(z));
|
|
|
|
if (z->n > y->n) {
|
|
duk__bigint *t;
|
|
t = y; y = z; z = t;
|
|
}
|
|
DUK_ASSERT(y->n >= z->n);
|
|
|
|
ny = y->n; nz = z->n;
|
|
carry = 0U;
|
|
for (i = 0; i < ny; i++) {
|
|
/* Carry is detected based on wrapping which relies on exact 32-bit
|
|
* types.
|
|
*/
|
|
DUK_ASSERT(i < DUK__BI_MAX_PARTS);
|
|
tmp1 = y->v[i];
|
|
tmp2 = tmp1;
|
|
if (i < nz) {
|
|
tmp2 += z->v[i];
|
|
}
|
|
|
|
/* Careful with carry condition:
|
|
* - If carry not added: 0x12345678 + 0 + 0xffffffff = 0x12345677 (< 0x12345678)
|
|
* - If carry added: 0x12345678 + 1 + 0xffffffff = 0x12345678 (== 0x12345678)
|
|
*/
|
|
if (carry) {
|
|
tmp2++;
|
|
carry = (tmp2 <= tmp1 ? 1U : 0U);
|
|
} else {
|
|
carry = (tmp2 < tmp1 ? 1U : 0U);
|
|
}
|
|
|
|
x->v[i] = tmp2;
|
|
}
|
|
if (carry) {
|
|
DUK_ASSERT(i < DUK__BI_MAX_PARTS);
|
|
DUK_ASSERT(carry == 1U);
|
|
x->v[i++] = carry;
|
|
}
|
|
x->n = i;
|
|
DUK_ASSERT(x->n <= DUK__BI_MAX_PARTS);
|
|
|
|
/* no need to normalize */
|
|
DUK_ASSERT(duk__bi_is_valid(x));
|
|
}
|
|
#endif /* DUK_USE_64BIT_OPS */
|
|
|
|
/* x <- y + z */
|
|
DUK_LOCAL void duk__bi_add_small(duk__bigint *x, duk__bigint *y, duk_uint32_t z) {
|
|
duk__bigint tmp;
|
|
|
|
DUK_ASSERT(duk__bi_is_valid(y));
|
|
|
|
/* XXX: this could be optimized; there is only one call site now though */
|
|
duk__bi_set_small(&tmp, z);
|
|
duk__bi_add(x, y, &tmp);
|
|
|
|
DUK_ASSERT(duk__bi_is_valid(x));
|
|
}
|
|
|
|
#if 0 /* unused */
|
|
/* x <- x + y, use t as temp */
|
|
DUK_LOCAL void duk__bi_add_copy(duk__bigint *x, duk__bigint *y, duk__bigint *t) {
|
|
duk__bi_add(t, x, y);
|
|
duk__bi_copy(x, t);
|
|
}
|
|
#endif
|
|
|
|
/* x <- y - z, require x >= y => z >= 0, i.e. y >= z */
|
|
#if defined(DUK_USE_64BIT_OPS)
|
|
DUK_LOCAL void duk__bi_sub(duk__bigint *x, duk__bigint *y, duk__bigint *z) {
|
|
duk_small_int_t i, ny, nz;
|
|
duk_uint32_t ty, tz;
|
|
duk_int64_t tmp;
|
|
|
|
DUK_ASSERT(duk__bi_is_valid(y));
|
|
DUK_ASSERT(duk__bi_is_valid(z));
|
|
DUK_ASSERT(duk__bi_compare(y, z) >= 0);
|
|
DUK_ASSERT(y->n >= z->n);
|
|
|
|
ny = y->n; nz = z->n;
|
|
tmp = 0;
|
|
for (i = 0; i < ny; i++) {
|
|
ty = y->v[i];
|
|
if (i < nz) {
|
|
tz = z->v[i];
|
|
} else {
|
|
tz = 0;
|
|
}
|
|
tmp = (duk_int64_t) ty - (duk_int64_t) tz + tmp;
|
|
x->v[i] = (duk_uint32_t) ((duk_uint64_t) tmp & 0xffffffffUL);
|
|
tmp = tmp >> 32; /* 0 or -1 */
|
|
}
|
|
DUK_ASSERT(tmp == 0);
|
|
|
|
x->n = i;
|
|
duk__bi_normalize(x); /* need to normalize, may even cancel to 0 */
|
|
DUK_ASSERT(duk__bi_is_valid(x));
|
|
}
|
|
#else
|
|
DUK_LOCAL void duk__bi_sub(duk__bigint *x, duk__bigint *y, duk__bigint *z) {
|
|
duk_small_int_t i, ny, nz;
|
|
duk_uint32_t tmp1, tmp2, borrow;
|
|
|
|
DUK_ASSERT(duk__bi_is_valid(y));
|
|
DUK_ASSERT(duk__bi_is_valid(z));
|
|
DUK_ASSERT(duk__bi_compare(y, z) >= 0);
|
|
DUK_ASSERT(y->n >= z->n);
|
|
|
|
ny = y->n; nz = z->n;
|
|
borrow = 0U;
|
|
for (i = 0; i < ny; i++) {
|
|
/* Borrow is detected based on wrapping which relies on exact 32-bit
|
|
* types.
|
|
*/
|
|
tmp1 = y->v[i];
|
|
tmp2 = tmp1;
|
|
if (i < nz) {
|
|
tmp2 -= z->v[i];
|
|
}
|
|
|
|
/* Careful with borrow condition:
|
|
* - If borrow not subtracted: 0x12345678 - 0 - 0xffffffff = 0x12345679 (> 0x12345678)
|
|
* - If borrow subtracted: 0x12345678 - 1 - 0xffffffff = 0x12345678 (== 0x12345678)
|
|
*/
|
|
if (borrow) {
|
|
tmp2--;
|
|
borrow = (tmp2 >= tmp1 ? 1U : 0U);
|
|
} else {
|
|
borrow = (tmp2 > tmp1 ? 1U : 0U);
|
|
}
|
|
|
|
x->v[i] = tmp2;
|
|
}
|
|
DUK_ASSERT(borrow == 0U);
|
|
|
|
x->n = i;
|
|
duk__bi_normalize(x); /* need to normalize, may even cancel to 0 */
|
|
DUK_ASSERT(duk__bi_is_valid(x));
|
|
}
|
|
#endif
|
|
|
|
#if 0 /* unused */
|
|
/* x <- y - z */
|
|
DUK_LOCAL void duk__bi_sub_small(duk__bigint *x, duk__bigint *y, duk_uint32_t z) {
|
|
duk__bigint tmp;
|
|
|
|
DUK_ASSERT(duk__bi_is_valid(y));
|
|
|
|
/* XXX: this could be optimized */
|
|
duk__bi_set_small(&tmp, z);
|
|
duk__bi_sub(x, y, &tmp);
|
|
|
|
DUK_ASSERT(duk__bi_is_valid(x));
|
|
}
|
|
#endif
|
|
|
|
/* x <- x - y, use t as temp */
|
|
DUK_LOCAL void duk__bi_sub_copy(duk__bigint *x, duk__bigint *y, duk__bigint *t) {
|
|
duk__bi_sub(t, x, y);
|
|
duk__bi_copy(x, t);
|
|
}
|
|
|
|
/* x <- y * z */
|
|
DUK_LOCAL void duk__bi_mul(duk__bigint *x, duk__bigint *y, duk__bigint *z) {
|
|
duk_small_int_t i, j, nx, nz;
|
|
|
|
DUK_ASSERT(duk__bi_is_valid(y));
|
|
DUK_ASSERT(duk__bi_is_valid(z));
|
|
|
|
nx = y->n + z->n; /* max possible */
|
|
DUK_ASSERT(nx <= DUK__BI_MAX_PARTS);
|
|
|
|
if (nx == 0) {
|
|
/* Both inputs are zero; cases where only one is zero can go
|
|
* through main algorithm.
|
|
*/
|
|
x->n = 0;
|
|
return;
|
|
}
|
|
|
|
duk_memzero((void *) x->v, (size_t) (sizeof(duk_uint32_t) * (size_t) nx));
|
|
x->n = nx;
|
|
|
|
nz = z->n;
|
|
for (i = 0; i < y->n; i++) {
|
|
#if defined(DUK_USE_64BIT_OPS)
|
|
duk_uint64_t tmp = 0U;
|
|
for (j = 0; j < nz; j++) {
|
|
tmp += (duk_uint64_t) y->v[i] * (duk_uint64_t) z->v[j] + x->v[i+j];
|
|
x->v[i+j] = (duk_uint32_t) (tmp & 0xffffffffUL);
|
|
tmp = tmp >> 32;
|
|
}
|
|
if (tmp > 0) {
|
|
DUK_ASSERT(i + j < nx);
|
|
DUK_ASSERT(i + j < DUK__BI_MAX_PARTS);
|
|
DUK_ASSERT(x->v[i+j] == 0U);
|
|
x->v[i+j] = (duk_uint32_t) tmp;
|
|
}
|
|
#else
|
|
/*
|
|
* Multiply + add + carry for 32-bit components using only 16x16->32
|
|
* multiplies and carry detection based on unsigned overflow.
|
|
*
|
|
* 1st mult, 32-bit: (A*2^16 + B)
|
|
* 2nd mult, 32-bit: (C*2^16 + D)
|
|
* 3rd add, 32-bit: E
|
|
* 4th add, 32-bit: F
|
|
*
|
|
* (AC*2^16 + B) * (C*2^16 + D) + E + F
|
|
* = AC*2^32 + AD*2^16 + BC*2^16 + BD + E + F
|
|
* = AC*2^32 + (AD + BC)*2^16 + (BD + E + F)
|
|
* = AC*2^32 + AD*2^16 + BC*2^16 + (BD + E + F)
|
|
*/
|
|
duk_uint32_t a, b, c, d, e, f;
|
|
duk_uint32_t r, s, t;
|
|
|
|
a = y->v[i]; b = a & 0xffffUL; a = a >> 16;
|
|
|
|
f = 0;
|
|
for (j = 0; j < nz; j++) {
|
|
c = z->v[j]; d = c & 0xffffUL; c = c >> 16;
|
|
e = x->v[i+j];
|
|
|
|
/* build result as: (r << 32) + s: start with (BD + E + F) */
|
|
r = 0;
|
|
s = b * d;
|
|
|
|
/* add E */
|
|
t = s + e;
|
|
if (t < s) { r++; } /* carry */
|
|
s = t;
|
|
|
|
/* add F */
|
|
t = s + f;
|
|
if (t < s) { r++; } /* carry */
|
|
s = t;
|
|
|
|
/* add BC*2^16 */
|
|
t = b * c;
|
|
r += (t >> 16);
|
|
t = s + ((t & 0xffffUL) << 16);
|
|
if (t < s) { r++; } /* carry */
|
|
s = t;
|
|
|
|
/* add AD*2^16 */
|
|
t = a * d;
|
|
r += (t >> 16);
|
|
t = s + ((t & 0xffffUL) << 16);
|
|
if (t < s) { r++; } /* carry */
|
|
s = t;
|
|
|
|
/* add AC*2^32 */
|
|
t = a * c;
|
|
r += t;
|
|
|
|
DUK_DDD(DUK_DDDPRINT("ab=%08lx cd=%08lx ef=%08lx -> rs=%08lx %08lx",
|
|
(unsigned long) y->v[i], (unsigned long) z->v[j],
|
|
(unsigned long) x->v[i+j], (unsigned long) r,
|
|
(unsigned long) s));
|
|
|
|
x->v[i+j] = s;
|
|
f = r;
|
|
}
|
|
if (f > 0U) {
|
|
DUK_ASSERT(i + j < nx);
|
|
DUK_ASSERT(i + j < DUK__BI_MAX_PARTS);
|
|
DUK_ASSERT(x->v[i+j] == 0U);
|
|
x->v[i+j] = (duk_uint32_t) f;
|
|
}
|
|
#endif /* DUK_USE_64BIT_OPS */
|
|
}
|
|
|
|
duk__bi_normalize(x);
|
|
DUK_ASSERT(duk__bi_is_valid(x));
|
|
}
|
|
|
|
/* x <- y * z */
|
|
DUK_LOCAL void duk__bi_mul_small(duk__bigint *x, duk__bigint *y, duk_uint32_t z) {
|
|
duk__bigint tmp;
|
|
|
|
DUK_ASSERT(duk__bi_is_valid(y));
|
|
|
|
/* XXX: this could be optimized */
|
|
duk__bi_set_small(&tmp, z);
|
|
duk__bi_mul(x, y, &tmp);
|
|
|
|
DUK_ASSERT(duk__bi_is_valid(x));
|
|
}
|
|
|
|
/* x <- x * y, use t as temp */
|
|
DUK_LOCAL void duk__bi_mul_copy(duk__bigint *x, duk__bigint *y, duk__bigint *t) {
|
|
duk__bi_mul(t, x, y);
|
|
duk__bi_copy(x, t);
|
|
}
|
|
|
|
/* x <- x * y, use t as temp */
|
|
DUK_LOCAL void duk__bi_mul_small_copy(duk__bigint *x, duk_uint32_t y, duk__bigint *t) {
|
|
duk__bi_mul_small(t, x, y);
|
|
duk__bi_copy(x, t);
|
|
}
|
|
|
|
DUK_LOCAL int duk__bi_is_even(duk__bigint *x) {
|
|
DUK_ASSERT(duk__bi_is_valid(x));
|
|
return (x->n == 0) || ((x->v[0] & 0x01) == 0);
|
|
}
|
|
|
|
DUK_LOCAL int duk__bi_is_zero(duk__bigint *x) {
|
|
DUK_ASSERT(duk__bi_is_valid(x));
|
|
return (x->n == 0); /* this is the case for normalized numbers */
|
|
}
|
|
|
|
/* Bigint is 2^52. Used to detect normalized IEEE double mantissa values
|
|
* which are at the lowest edge (next floating point value downwards has
|
|
* a different exponent). The lowest mantissa has the form:
|
|
*
|
|
* 1000........000 (52 zeroes; only "hidden bit" is set)
|
|
*/
|
|
DUK_LOCAL duk_small_int_t duk__bi_is_2to52(duk__bigint *x) {
|
|
DUK_ASSERT(duk__bi_is_valid(x));
|
|
return (duk_small_int_t)
|
|
(x->n == 2) && (x->v[0] == 0U) && (x->v[1] == (1U << (52-32)));
|
|
}
|
|
|
|
/* x <- (1<<y) */
|
|
DUK_LOCAL void duk__bi_twoexp(duk__bigint *x, duk_small_int_t y) {
|
|
duk_small_int_t n, r;
|
|
|
|
n = (y / 32) + 1;
|
|
DUK_ASSERT(n > 0);
|
|
r = y % 32;
|
|
duk_memzero((void *) x->v, sizeof(duk_uint32_t) * (size_t) n);
|
|
x->n = n;
|
|
x->v[n - 1] = (((duk_uint32_t) 1) << r);
|
|
}
|
|
|
|
/* x <- b^y; use t1 and t2 as temps */
|
|
DUK_LOCAL void duk__bi_exp_small(duk__bigint *x, duk_small_int_t b, duk_small_int_t y, duk__bigint *t1, duk__bigint *t2) {
|
|
/* Fast path the binary case */
|
|
|
|
DUK_ASSERT(x != t1 && x != t2 && t1 != t2); /* distinct bignums, easy mistake to make */
|
|
DUK_ASSERT(b >= 0);
|
|
DUK_ASSERT(y >= 0);
|
|
|
|
if (b == 2) {
|
|
duk__bi_twoexp(x, y);
|
|
return;
|
|
}
|
|
|
|
/* http://en.wikipedia.org/wiki/Exponentiation_by_squaring */
|
|
|
|
DUK_DDD(DUK_DDDPRINT("exp_small: b=%ld, y=%ld", (long) b, (long) y));
|
|
|
|
duk__bi_set_small(x, 1);
|
|
duk__bi_set_small(t1, (duk_uint32_t) b);
|
|
for (;;) {
|
|
/* Loop structure ensures that we don't compute t1^2 unnecessarily
|
|
* on the final round, as that might create a bignum exceeding the
|
|
* current DUK__BI_MAX_PARTS limit.
|
|
*/
|
|
if (y & 0x01) {
|
|
duk__bi_mul_copy(x, t1, t2);
|
|
}
|
|
y = y >> 1;
|
|
if (y == 0) {
|
|
break;
|
|
}
|
|
duk__bi_mul_copy(t1, t1, t2);
|
|
}
|
|
|
|
DUK__BI_PRINT("exp_small result", x);
|
|
}
|
|
|
|
/*
|
|
* A Dragon4 number-to-string variant, based on:
|
|
*
|
|
* Guy L. Steele Jr., Jon L. White: "How to Print Floating-Point Numbers
|
|
* Accurately"
|
|
*
|
|
* Robert G. Burger, R. Kent Dybvig: "Printing Floating-Point Numbers
|
|
* Quickly and Accurately"
|
|
*
|
|
* The current algorithm is based on Figure 1 of the Burger-Dybvig paper,
|
|
* i.e. the base implementation without logarithm estimation speedups
|
|
* (these would increase code footprint considerably). Fixed-format output
|
|
* does not follow the suggestions in the paper; instead, we generate an
|
|
* extra digit and round-with-carry.
|
|
*
|
|
* The same algorithm is used for number parsing (with b=10 and B=2)
|
|
* by generating one extra digit and doing rounding manually.
|
|
*
|
|
* See doc/number-conversion.rst for limitations.
|
|
*/
|
|
|
|
/* Maximum number of digits generated. */
|
|
#define DUK__MAX_OUTPUT_DIGITS 1040 /* (Number.MAX_VALUE).toString(2).length == 1024, + slack */
|
|
|
|
/* Maximum number of characters in formatted value. */
|
|
#define DUK__MAX_FORMATTED_LENGTH 1040 /* (-Number.MAX_VALUE).toString(2).length == 1025, + slack */
|
|
|
|
/* Number and (minimum) size of bigints in the nc_ctx structure. */
|
|
#define DUK__NUMCONV_CTX_NUM_BIGINTS 7
|
|
#define DUK__NUMCONV_CTX_BIGINTS_SIZE (sizeof(duk__bigint) * DUK__NUMCONV_CTX_NUM_BIGINTS)
|
|
|
|
typedef struct {
|
|
/* Currently about 7*152 = 1064 bytes. The space for these
|
|
* duk__bigints is used also as a temporary buffer for generating
|
|
* the final string. This is a bit awkard; a union would be
|
|
* more correct.
|
|
*/
|
|
duk__bigint f, r, s, mp, mm, t1, t2;
|
|
|
|
duk_small_int_t is_s2n; /* if 1, doing a string-to-number; else doing a number-to-string */
|
|
duk_small_int_t is_fixed; /* if 1, doing a fixed format output (not free format) */
|
|
duk_small_int_t req_digits; /* requested number of output digits; 0 = free-format */
|
|
duk_small_int_t abs_pos; /* digit position is absolute, not relative */
|
|
duk_small_int_t e; /* exponent for 'f' */
|
|
duk_small_int_t b; /* input radix */
|
|
duk_small_int_t B; /* output radix */
|
|
duk_small_int_t k; /* see algorithm */
|
|
duk_small_int_t low_ok; /* see algorithm */
|
|
duk_small_int_t high_ok; /* see algorithm */
|
|
duk_small_int_t unequal_gaps; /* m+ != m- (very rarely) */
|
|
|
|
/* Buffer used for generated digits, values are in the range [0,B-1]. */
|
|
duk_uint8_t digits[DUK__MAX_OUTPUT_DIGITS];
|
|
duk_small_int_t count; /* digit count */
|
|
} duk__numconv_stringify_ctx;
|
|
|
|
/* Note: computes with 'idx' in assertions, so caller beware.
|
|
* 'idx' is preincremented, i.e. '1' on first call, because it
|
|
* is more convenient for the caller.
|
|
*/
|
|
#define DUK__DRAGON4_OUTPUT_PREINC(nc_ctx,preinc_idx,x) do { \
|
|
DUK_ASSERT((preinc_idx) - 1 >= 0); \
|
|
DUK_ASSERT((preinc_idx) - 1 < DUK__MAX_OUTPUT_DIGITS); \
|
|
((nc_ctx)->digits[(preinc_idx) - 1]) = (duk_uint8_t) (x); \
|
|
} while (0)
|
|
|
|
DUK_LOCAL duk_size_t duk__dragon4_format_uint32(duk_uint8_t *buf, duk_uint32_t x, duk_small_int_t radix) {
|
|
duk_uint8_t *p;
|
|
duk_size_t len;
|
|
duk_small_int_t dig;
|
|
duk_uint32_t t;
|
|
|
|
DUK_ASSERT(buf != NULL);
|
|
DUK_ASSERT(radix >= 2 && radix <= 36);
|
|
|
|
/* A 32-bit unsigned integer formats to at most 32 digits (the
|
|
* worst case happens with radix == 2). Output the digits backwards,
|
|
* and use a memmove() to get them in the right place.
|
|
*/
|
|
|
|
p = buf + 32;
|
|
for (;;) {
|
|
t = x / (duk_uint32_t) radix;
|
|
dig = (duk_small_int_t) (x - t * (duk_uint32_t) radix);
|
|
x = t;
|
|
|
|
DUK_ASSERT(dig >= 0 && dig < 36);
|
|
*(--p) = DUK__DIGITCHAR(dig);
|
|
|
|
if (x == 0) {
|
|
break;
|
|
}
|
|
}
|
|
len = (duk_size_t) ((buf + 32) - p);
|
|
|
|
duk_memmove((void *) buf, (const void *) p, (size_t) len);
|
|
|
|
return len;
|
|
}
|
|
|
|
DUK_LOCAL void duk__dragon4_prepare(duk__numconv_stringify_ctx *nc_ctx) {
|
|
duk_small_int_t lowest_mantissa;
|
|
|
|
#if 1
|
|
/* Assume IEEE round-to-even, so that shorter encoding can be used
|
|
* when round-to-even would produce correct result. By removing
|
|
* this check (and having low_ok == high_ok == 0) the results would
|
|
* still be accurate but in some cases longer than necessary.
|
|
*/
|
|
if (duk__bi_is_even(&nc_ctx->f)) {
|
|
DUK_DDD(DUK_DDDPRINT("f is even"));
|
|
nc_ctx->low_ok = 1;
|
|
nc_ctx->high_ok = 1;
|
|
} else {
|
|
DUK_DDD(DUK_DDDPRINT("f is odd"));
|
|
nc_ctx->low_ok = 0;
|
|
nc_ctx->high_ok = 0;
|
|
}
|
|
#else
|
|
/* Note: not honoring round-to-even should work but now generates incorrect
|
|
* results. For instance, 1e23 serializes to "a000...", i.e. the first digit
|
|
* equals the radix (10). Scaling stops one step too early in this case.
|
|
* Don't know why this is the case, but since this code path is unused, it
|
|
* doesn't matter.
|
|
*/
|
|
nc_ctx->low_ok = 0;
|
|
nc_ctx->high_ok = 0;
|
|
#endif
|
|
|
|
/* For string-to-number, pretend we never have the lowest mantissa as there
|
|
* is no natural "precision" for inputs. Having lowest_mantissa == 0, we'll
|
|
* fall into the base cases for both e >= 0 and e < 0.
|
|
*/
|
|
if (nc_ctx->is_s2n) {
|
|
lowest_mantissa = 0;
|
|
} else {
|
|
lowest_mantissa = duk__bi_is_2to52(&nc_ctx->f);
|
|
}
|
|
|
|
nc_ctx->unequal_gaps = 0;
|
|
if (nc_ctx->e >= 0) {
|
|
/* exponent non-negative (and thus not minimum exponent) */
|
|
|
|
if (lowest_mantissa) {
|
|
/* (>= e 0) AND (= f (expt b (- p 1)))
|
|
*
|
|
* be <- (expt b e) == b^e
|
|
* be1 <- (* be b) == (expt b (+ e 1)) == b^(e+1)
|
|
* r <- (* f be1 2) == 2 * f * b^(e+1) [if b==2 -> f * b^(e+2)]
|
|
* s <- (* b 2) [if b==2 -> 4]
|
|
* m+ <- be1 == b^(e+1)
|
|
* m- <- be == b^e
|
|
* k <- 0
|
|
* B <- B
|
|
* low_ok <- round
|
|
* high_ok <- round
|
|
*/
|
|
|
|
DUK_DDD(DUK_DDDPRINT("non-negative exponent (not smallest exponent); "
|
|
"lowest mantissa value for this exponent -> "
|
|
"unequal gaps"));
|
|
|
|
duk__bi_exp_small(&nc_ctx->mm, nc_ctx->b, nc_ctx->e, &nc_ctx->t1, &nc_ctx->t2); /* mm <- b^e */
|
|
duk__bi_mul_small(&nc_ctx->mp, &nc_ctx->mm, (duk_uint32_t) nc_ctx->b); /* mp <- b^(e+1) */
|
|
duk__bi_mul_small(&nc_ctx->t1, &nc_ctx->f, 2);
|
|
duk__bi_mul(&nc_ctx->r, &nc_ctx->t1, &nc_ctx->mp); /* r <- (2 * f) * b^(e+1) */
|
|
duk__bi_set_small(&nc_ctx->s, (duk_uint32_t) (nc_ctx->b * 2)); /* s <- 2 * b */
|
|
nc_ctx->unequal_gaps = 1;
|
|
} else {
|
|
/* (>= e 0) AND (not (= f (expt b (- p 1))))
|
|
*
|
|
* be <- (expt b e) == b^e
|
|
* r <- (* f be 2) == 2 * f * b^e [if b==2 -> f * b^(e+1)]
|
|
* s <- 2
|
|
* m+ <- be == b^e
|
|
* m- <- be == b^e
|
|
* k <- 0
|
|
* B <- B
|
|
* low_ok <- round
|
|
* high_ok <- round
|
|
*/
|
|
|
|
DUK_DDD(DUK_DDDPRINT("non-negative exponent (not smallest exponent); "
|
|
"not lowest mantissa for this exponent -> "
|
|
"equal gaps"));
|
|
|
|
duk__bi_exp_small(&nc_ctx->mm, nc_ctx->b, nc_ctx->e, &nc_ctx->t1, &nc_ctx->t2); /* mm <- b^e */
|
|
duk__bi_copy(&nc_ctx->mp, &nc_ctx->mm); /* mp <- b^e */
|
|
duk__bi_mul_small(&nc_ctx->t1, &nc_ctx->f, 2);
|
|
duk__bi_mul(&nc_ctx->r, &nc_ctx->t1, &nc_ctx->mp); /* r <- (2 * f) * b^e */
|
|
duk__bi_set_small(&nc_ctx->s, 2); /* s <- 2 */
|
|
}
|
|
} else {
|
|
/* When doing string-to-number, lowest_mantissa is always 0 so
|
|
* the exponent check, while incorrect, won't matter.
|
|
*/
|
|
if (nc_ctx->e > DUK__IEEE_DOUBLE_EXP_MIN /*not minimum exponent*/ &&
|
|
lowest_mantissa /* lowest mantissa for this exponent*/) {
|
|
/* r <- (* f b 2) [if b==2 -> (* f 4)]
|
|
* s <- (* (expt b (- 1 e)) 2) == b^(1-e) * 2 [if b==2 -> b^(2-e)]
|
|
* m+ <- b == 2
|
|
* m- <- 1
|
|
* k <- 0
|
|
* B <- B
|
|
* low_ok <- round
|
|
* high_ok <- round
|
|
*/
|
|
|
|
DUK_DDD(DUK_DDDPRINT("negative exponent; not minimum exponent and "
|
|
"lowest mantissa for this exponent -> "
|
|
"unequal gaps"));
|
|
|
|
duk__bi_mul_small(&nc_ctx->r, &nc_ctx->f, (duk_uint32_t) (nc_ctx->b * 2)); /* r <- (2 * b) * f */
|
|
duk__bi_exp_small(&nc_ctx->t1, nc_ctx->b, 1 - nc_ctx->e, &nc_ctx->s, &nc_ctx->t2); /* NB: use 's' as temp on purpose */
|
|
duk__bi_mul_small(&nc_ctx->s, &nc_ctx->t1, 2); /* s <- b^(1-e) * 2 */
|
|
duk__bi_set_small(&nc_ctx->mp, 2);
|
|
duk__bi_set_small(&nc_ctx->mm, 1);
|
|
nc_ctx->unequal_gaps = 1;
|
|
} else {
|
|
/* r <- (* f 2)
|
|
* s <- (* (expt b (- e)) 2) == b^(-e) * 2 [if b==2 -> b^(1-e)]
|
|
* m+ <- 1
|
|
* m- <- 1
|
|
* k <- 0
|
|
* B <- B
|
|
* low_ok <- round
|
|
* high_ok <- round
|
|
*/
|
|
|
|
DUK_DDD(DUK_DDDPRINT("negative exponent; minimum exponent or not "
|
|
"lowest mantissa for this exponent -> "
|
|
"equal gaps"));
|
|
|
|
duk__bi_mul_small(&nc_ctx->r, &nc_ctx->f, 2); /* r <- 2 * f */
|
|
duk__bi_exp_small(&nc_ctx->t1, nc_ctx->b, -nc_ctx->e, &nc_ctx->s, &nc_ctx->t2); /* NB: use 's' as temp on purpose */
|
|
duk__bi_mul_small(&nc_ctx->s, &nc_ctx->t1, 2); /* s <- b^(-e) * 2 */
|
|
duk__bi_set_small(&nc_ctx->mp, 1);
|
|
duk__bi_set_small(&nc_ctx->mm, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
DUK_LOCAL void duk__dragon4_scale(duk__numconv_stringify_ctx *nc_ctx) {
|
|
duk_small_int_t k = 0;
|
|
|
|
/* This is essentially the 'scale' algorithm, with recursion removed.
|
|
* Note that 'k' is either correct immediately, or will move in one
|
|
* direction in the loop. There's no need to do the low/high checks
|
|
* on every round (like the Scheme algorithm does).
|
|
*
|
|
* The scheme algorithm finds 'k' and updates 's' simultaneously,
|
|
* while the logical algorithm finds 'k' with 's' having its initial
|
|
* value, after which 's' is updated separately (see the Burger-Dybvig
|
|
* paper, Section 3.1, steps 2 and 3).
|
|
*
|
|
* The case where m+ == m- (almost always) is optimized for, because
|
|
* it reduces the bigint operations considerably and almost always
|
|
* applies. The scale loop only needs to work with m+, so this works.
|
|
*/
|
|
|
|
/* XXX: this algorithm could be optimized quite a lot by using e.g.
|
|
* a logarithm based estimator for 'k' and performing B^n multiplication
|
|
* using a lookup table or using some bit-representation based exp
|
|
* algorithm. Currently we just loop, with significant performance
|
|
* impact for very large and very small numbers.
|
|
*/
|
|
|
|
DUK_DDD(DUK_DDDPRINT("scale: B=%ld, low_ok=%ld, high_ok=%ld",
|
|
(long) nc_ctx->B, (long) nc_ctx->low_ok, (long) nc_ctx->high_ok));
|
|
DUK__BI_PRINT("r(init)", &nc_ctx->r);
|
|
DUK__BI_PRINT("s(init)", &nc_ctx->s);
|
|
DUK__BI_PRINT("mp(init)", &nc_ctx->mp);
|
|
DUK__BI_PRINT("mm(init)", &nc_ctx->mm);
|
|
|
|
for (;;) {
|
|
DUK_DDD(DUK_DDDPRINT("scale loop (inc k), k=%ld", (long) k));
|
|
DUK__BI_PRINT("r", &nc_ctx->r);
|
|
DUK__BI_PRINT("s", &nc_ctx->s);
|
|
DUK__BI_PRINT("m+", &nc_ctx->mp);
|
|
DUK__BI_PRINT("m-", &nc_ctx->mm);
|
|
|
|
duk__bi_add(&nc_ctx->t1, &nc_ctx->r, &nc_ctx->mp); /* t1 = (+ r m+) */
|
|
if (duk__bi_compare(&nc_ctx->t1, &nc_ctx->s) >= (nc_ctx->high_ok ? 0 : 1)) {
|
|
DUK_DDD(DUK_DDDPRINT("k is too low"));
|
|
/* r <- r
|
|
* s <- (* s B)
|
|
* m+ <- m+
|
|
* m- <- m-
|
|
* k <- (+ k 1)
|
|
*/
|
|
|
|
duk__bi_mul_small_copy(&nc_ctx->s, (duk_uint32_t) nc_ctx->B, &nc_ctx->t1);
|
|
k++;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* k > 0 -> k was too low, and cannot be too high */
|
|
if (k > 0) {
|
|
goto skip_dec_k;
|
|
}
|
|
|
|
for (;;) {
|
|
DUK_DDD(DUK_DDDPRINT("scale loop (dec k), k=%ld", (long) k));
|
|
DUK__BI_PRINT("r", &nc_ctx->r);
|
|
DUK__BI_PRINT("s", &nc_ctx->s);
|
|
DUK__BI_PRINT("m+", &nc_ctx->mp);
|
|
DUK__BI_PRINT("m-", &nc_ctx->mm);
|
|
|
|
duk__bi_add(&nc_ctx->t1, &nc_ctx->r, &nc_ctx->mp); /* t1 = (+ r m+) */
|
|
duk__bi_mul_small(&nc_ctx->t2, &nc_ctx->t1, (duk_uint32_t) nc_ctx->B); /* t2 = (* (+ r m+) B) */
|
|
if (duk__bi_compare(&nc_ctx->t2, &nc_ctx->s) <= (nc_ctx->high_ok ? -1 : 0)) {
|
|
DUK_DDD(DUK_DDDPRINT("k is too high"));
|
|
/* r <- (* r B)
|
|
* s <- s
|
|
* m+ <- (* m+ B)
|
|
* m- <- (* m- B)
|
|
* k <- (- k 1)
|
|
*/
|
|
duk__bi_mul_small_copy(&nc_ctx->r, (duk_uint32_t) nc_ctx->B, &nc_ctx->t1);
|
|
duk__bi_mul_small_copy(&nc_ctx->mp, (duk_uint32_t) nc_ctx->B, &nc_ctx->t1);
|
|
if (nc_ctx->unequal_gaps) {
|
|
DUK_DDD(DUK_DDDPRINT("m+ != m- -> need to update m- too"));
|
|
duk__bi_mul_small_copy(&nc_ctx->mm, (duk_uint32_t) nc_ctx->B, &nc_ctx->t1);
|
|
}
|
|
k--;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
skip_dec_k:
|
|
|
|
if (!nc_ctx->unequal_gaps) {
|
|
DUK_DDD(DUK_DDDPRINT("equal gaps, copy m- from m+"));
|
|
duk__bi_copy(&nc_ctx->mm, &nc_ctx->mp); /* mm <- mp */
|
|
}
|
|
nc_ctx->k = k;
|
|
|
|
DUK_DDD(DUK_DDDPRINT("final k: %ld", (long) k));
|
|
DUK__BI_PRINT("r(final)", &nc_ctx->r);
|
|
DUK__BI_PRINT("s(final)", &nc_ctx->s);
|
|
DUK__BI_PRINT("mp(final)", &nc_ctx->mp);
|
|
DUK__BI_PRINT("mm(final)", &nc_ctx->mm);
|
|
}
|
|
|
|
DUK_LOCAL void duk__dragon4_generate(duk__numconv_stringify_ctx *nc_ctx) {
|
|
duk_small_int_t tc1, tc2; /* terminating conditions */
|
|
duk_small_int_t d; /* current digit */
|
|
duk_small_int_t count = 0; /* digit count */
|
|
|
|
/*
|
|
* Digit generation loop.
|
|
*
|
|
* Different termination conditions:
|
|
*
|
|
* 1. Free format output. Terminate when shortest accurate
|
|
* representation found.
|
|
*
|
|
* 2. Fixed format output, with specific number of digits.
|
|
* Ignore termination conditions, terminate when digits
|
|
* generated. Caller requests an extra digit and rounds.
|
|
*
|
|
* 3. Fixed format output, with a specific absolute cut-off
|
|
* position (e.g. 10 digits after decimal point). Note
|
|
* that we always generate at least one digit, even if
|
|
* the digit is below the cut-off point already.
|
|
*/
|
|
|
|
for (;;) {
|
|
DUK_DDD(DUK_DDDPRINT("generate loop, count=%ld, k=%ld, B=%ld, low_ok=%ld, high_ok=%ld",
|
|
(long) count, (long) nc_ctx->k, (long) nc_ctx->B,
|
|
(long) nc_ctx->low_ok, (long) nc_ctx->high_ok));
|
|
DUK__BI_PRINT("r", &nc_ctx->r);
|
|
DUK__BI_PRINT("s", &nc_ctx->s);
|
|
DUK__BI_PRINT("m+", &nc_ctx->mp);
|
|
DUK__BI_PRINT("m-", &nc_ctx->mm);
|
|
|
|
/* (quotient-remainder (* r B) s) using a dummy subtraction loop */
|
|
duk__bi_mul_small(&nc_ctx->t1, &nc_ctx->r, (duk_uint32_t) nc_ctx->B); /* t1 <- (* r B) */
|
|
d = 0;
|
|
for (;;) {
|
|
if (duk__bi_compare(&nc_ctx->t1, &nc_ctx->s) < 0) {
|
|
break;
|
|
}
|
|
duk__bi_sub_copy(&nc_ctx->t1, &nc_ctx->s, &nc_ctx->t2); /* t1 <- t1 - s */
|
|
d++;
|
|
}
|
|
duk__bi_copy(&nc_ctx->r, &nc_ctx->t1); /* r <- (remainder (* r B) s) */
|
|
/* d <- (quotient (* r B) s) (in range 0...B-1) */
|
|
DUK_DDD(DUK_DDDPRINT("-> d(quot)=%ld", (long) d));
|
|
DUK__BI_PRINT("r(rem)", &nc_ctx->r);
|
|
|
|
duk__bi_mul_small_copy(&nc_ctx->mp, (duk_uint32_t) nc_ctx->B, &nc_ctx->t2); /* m+ <- (* m+ B) */
|
|
duk__bi_mul_small_copy(&nc_ctx->mm, (duk_uint32_t) nc_ctx->B, &nc_ctx->t2); /* m- <- (* m- B) */
|
|
DUK__BI_PRINT("mp(upd)", &nc_ctx->mp);
|
|
DUK__BI_PRINT("mm(upd)", &nc_ctx->mm);
|
|
|
|
/* Terminating conditions. For fixed width output, we just ignore the
|
|
* terminating conditions (and pretend that tc1 == tc2 == false). The
|
|
* the current shortcut for fixed-format output is to generate a few
|
|
* extra digits and use rounding (with carry) to finish the output.
|
|
*/
|
|
|
|
if (nc_ctx->is_fixed == 0) {
|
|
/* free-form */
|
|
tc1 = (duk__bi_compare(&nc_ctx->r, &nc_ctx->mm) <= (nc_ctx->low_ok ? 0 : -1));
|
|
|
|
duk__bi_add(&nc_ctx->t1, &nc_ctx->r, &nc_ctx->mp); /* t1 <- (+ r m+) */
|
|
tc2 = (duk__bi_compare(&nc_ctx->t1, &nc_ctx->s) >= (nc_ctx->high_ok ? 0 : 1));
|
|
|
|
DUK_DDD(DUK_DDDPRINT("tc1=%ld, tc2=%ld", (long) tc1, (long) tc2));
|
|
} else {
|
|
/* fixed-format */
|
|
tc1 = 0;
|
|
tc2 = 0;
|
|
}
|
|
|
|
/* Count is incremented before DUK__DRAGON4_OUTPUT_PREINC() call
|
|
* on purpose, which is taken into account by the macro.
|
|
*/
|
|
count++;
|
|
|
|
if (tc1) {
|
|
if (tc2) {
|
|
/* tc1 = true, tc2 = true */
|
|
duk__bi_mul_small(&nc_ctx->t1, &nc_ctx->r, 2);
|
|
if (duk__bi_compare(&nc_ctx->t1, &nc_ctx->s) < 0) { /* (< (* r 2) s) */
|
|
DUK_DDD(DUK_DDDPRINT("tc1=true, tc2=true, 2r > s: output d --> %ld (k=%ld)",
|
|
(long) d, (long) nc_ctx->k));
|
|
DUK__DRAGON4_OUTPUT_PREINC(nc_ctx, count, d);
|
|
} else {
|
|
DUK_DDD(DUK_DDDPRINT("tc1=true, tc2=true, 2r <= s: output d+1 --> %ld (k=%ld)",
|
|
(long) (d + 1), (long) nc_ctx->k));
|
|
DUK__DRAGON4_OUTPUT_PREINC(nc_ctx, count, d + 1);
|
|
}
|
|
break;
|
|
} else {
|
|
/* tc1 = true, tc2 = false */
|
|
DUK_DDD(DUK_DDDPRINT("tc1=true, tc2=false: output d --> %ld (k=%ld)",
|
|
(long) d, (long) nc_ctx->k));
|
|
DUK__DRAGON4_OUTPUT_PREINC(nc_ctx, count, d);
|
|
break;
|
|
}
|
|
} else {
|
|
if (tc2) {
|
|
/* tc1 = false, tc2 = true */
|
|
DUK_DDD(DUK_DDDPRINT("tc1=false, tc2=true: output d+1 --> %ld (k=%ld)",
|
|
(long) (d + 1), (long) nc_ctx->k));
|
|
DUK__DRAGON4_OUTPUT_PREINC(nc_ctx, count, d + 1);
|
|
break;
|
|
} else {
|
|
/* tc1 = false, tc2 = false */
|
|
DUK_DDD(DUK_DDDPRINT("tc1=false, tc2=false: output d --> %ld (k=%ld)",
|
|
(long) d, (long) nc_ctx->k));
|
|
DUK__DRAGON4_OUTPUT_PREINC(nc_ctx, count, d);
|
|
|
|
/* r <- r (updated above: r <- (remainder (* r B) s)
|
|
* s <- s
|
|
* m+ <- m+ (updated above: m+ <- (* m+ B)
|
|
* m- <- m- (updated above: m- <- (* m- B)
|
|
* B, low_ok, high_ok are fixed
|
|
*/
|
|
|
|
/* fall through and continue for-loop */
|
|
}
|
|
}
|
|
|
|
/* fixed-format termination conditions */
|
|
if (nc_ctx->is_fixed) {
|
|
if (nc_ctx->abs_pos) {
|
|
int pos = nc_ctx->k - count + 1; /* count is already incremented, take into account */
|
|
DUK_DDD(DUK_DDDPRINT("fixed format, absolute: abs pos=%ld, k=%ld, count=%ld, req=%ld",
|
|
(long) pos, (long) nc_ctx->k, (long) count, (long) nc_ctx->req_digits));
|
|
if (pos <= nc_ctx->req_digits) {
|
|
DUK_DDD(DUK_DDDPRINT("digit position reached req_digits, end generate loop"));
|
|
break;
|
|
}
|
|
} else {
|
|
DUK_DDD(DUK_DDDPRINT("fixed format, relative: k=%ld, count=%ld, req=%ld",
|
|
(long) nc_ctx->k, (long) count, (long) nc_ctx->req_digits));
|
|
if (count >= nc_ctx->req_digits) {
|
|
DUK_DDD(DUK_DDDPRINT("digit count reached req_digits, end generate loop"));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} /* for */
|
|
|
|
nc_ctx->count = count;
|
|
|
|
DUK_DDD(DUK_DDDPRINT("generate finished"));
|
|
|
|
#if defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 2)
|
|
{
|
|
duk_uint8_t buf[2048];
|
|
duk_small_int_t i, t;
|
|
duk_memzero(buf, sizeof(buf));
|
|
for (i = 0; i < nc_ctx->count; i++) {
|
|
t = nc_ctx->digits[i];
|
|
if (t < 0 || t > 36) {
|
|
buf[i] = (duk_uint8_t) '?';
|
|
} else {
|
|
buf[i] = (duk_uint8_t) DUK__DIGITCHAR(t);
|
|
}
|
|
}
|
|
DUK_DDD(DUK_DDDPRINT("-> generated digits; k=%ld, digits='%s'",
|
|
(long) nc_ctx->k, (const char *) buf));
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* Round up digits to a given position. If position is out-of-bounds,
|
|
* does nothing. If carry propagates over the first digit, a '1' is
|
|
* prepended to digits and 'k' will be updated. Return value indicates
|
|
* whether carry propagated over the first digit.
|
|
*
|
|
* Note that nc_ctx->count is NOT updated based on the rounding position
|
|
* (it is updated only if carry overflows over the first digit and an
|
|
* extra digit is prepended).
|
|
*/
|
|
DUK_LOCAL duk_small_int_t duk__dragon4_fixed_format_round(duk__numconv_stringify_ctx *nc_ctx, duk_small_int_t round_idx) {
|
|
duk_small_int_t t;
|
|
duk_uint8_t *p;
|
|
duk_uint8_t roundup_limit;
|
|
duk_small_int_t ret = 0;
|
|
|
|
/*
|
|
* round_idx points to the digit which is considered for rounding; the
|
|
* digit to its left is the final digit of the rounded value. If round_idx
|
|
* is zero, rounding will be performed; the result will either be an empty
|
|
* rounded value or if carry happens a '1' digit is generated.
|
|
*/
|
|
|
|
if (round_idx >= nc_ctx->count) {
|
|
DUK_DDD(DUK_DDDPRINT("round_idx out of bounds (%ld >= %ld (count)) -> no rounding",
|
|
(long) round_idx, (long) nc_ctx->count));
|
|
return 0;
|
|
} else if (round_idx < 0) {
|
|
DUK_DDD(DUK_DDDPRINT("round_idx out of bounds (%ld < 0) -> no rounding",
|
|
(long) round_idx));
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Round-up limit.
|
|
*
|
|
* For even values, divides evenly, e.g. 10 -> roundup_limit=5.
|
|
*
|
|
* For odd values, rounds up, e.g. 3 -> roundup_limit=2.
|
|
* If radix is 3, 0/3 -> down, 1/3 -> down, 2/3 -> up.
|
|
*/
|
|
roundup_limit = (duk_uint8_t) ((nc_ctx->B + 1) / 2);
|
|
|
|
p = &nc_ctx->digits[round_idx];
|
|
if (*p >= roundup_limit) {
|
|
DUK_DDD(DUK_DDDPRINT("fixed-format rounding carry required"));
|
|
/* carry */
|
|
for (;;) {
|
|
*p = 0;
|
|
if (p == &nc_ctx->digits[0]) {
|
|
DUK_DDD(DUK_DDDPRINT("carry propagated to first digit -> special case handling"));
|
|
duk_memmove((void *) (&nc_ctx->digits[1]),
|
|
(const void *) (&nc_ctx->digits[0]),
|
|
(size_t) (sizeof(char) * (size_t) nc_ctx->count));
|
|
nc_ctx->digits[0] = 1; /* don't increase 'count' */
|
|
nc_ctx->k++; /* position of highest digit changed */
|
|
nc_ctx->count++; /* number of digits changed */
|
|
ret = 1;
|
|
break;
|
|
}
|
|
|
|
DUK_DDD(DUK_DDDPRINT("fixed-format rounding carry: B=%ld, roundup_limit=%ld, p=%p, digits=%p",
|
|
(long) nc_ctx->B, (long) roundup_limit, (void *) p, (void *) nc_ctx->digits));
|
|
p--;
|
|
t = *p;
|
|
DUK_DDD(DUK_DDDPRINT("digit before carry: %ld", (long) t));
|
|
if (++t < nc_ctx->B) {
|
|
DUK_DDD(DUK_DDDPRINT("rounding carry terminated"));
|
|
*p = (duk_uint8_t) t;
|
|
break;
|
|
}
|
|
|
|
DUK_DDD(DUK_DDDPRINT("wraps, carry to next digit"));
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
#define DUK__NO_EXP (65536) /* arbitrary marker, outside valid exp range */
|
|
|
|
DUK_LOCAL void duk__dragon4_convert_and_push(duk__numconv_stringify_ctx *nc_ctx,
|
|
duk_hthread *thr,
|
|
duk_small_int_t radix,
|
|
duk_small_int_t digits,
|
|
duk_small_uint_t flags,
|
|
duk_small_int_t neg) {
|
|
duk_small_int_t k;
|
|
duk_small_int_t pos, pos_end;
|
|
duk_small_int_t expt;
|
|
duk_small_int_t dig;
|
|
duk_uint8_t *q;
|
|
duk_uint8_t *buf;
|
|
|
|
/*
|
|
* The string conversion here incorporates all the necessary ECMAScript
|
|
* semantics without attempting to be generic. nc_ctx->digits contains
|
|
* nc_ctx->count digits (>= 1), with the topmost digit's 'position'
|
|
* indicated by nc_ctx->k as follows:
|
|
*
|
|
* digits="123" count=3 k=0 --> 0.123
|
|
* digits="123" count=3 k=1 --> 1.23
|
|
* digits="123" count=3 k=5 --> 12300
|
|
* digits="123" count=3 k=-1 --> 0.0123
|
|
*
|
|
* Note that the identifier names used for format selection are different
|
|
* in Burger-Dybvig paper and ECMAScript specification (quite confusingly
|
|
* so, because e.g. 'k' has a totally different meaning in each). See
|
|
* documentation for discussion.
|
|
*
|
|
* ECMAScript doesn't specify any specific behavior for format selection
|
|
* (e.g. when to use exponent notation) for non-base-10 numbers.
|
|
*
|
|
* The bigint space in the context is reused for string output, as there
|
|
* is more than enough space for that (>1kB at the moment), and we avoid
|
|
* allocating even more stack.
|
|
*/
|
|
|
|
DUK_ASSERT(DUK__NUMCONV_CTX_BIGINTS_SIZE >= DUK__MAX_FORMATTED_LENGTH);
|
|
DUK_ASSERT(nc_ctx->count >= 1);
|
|
|
|
k = nc_ctx->k;
|
|
buf = (duk_uint8_t *) &nc_ctx->f; /* XXX: union would be more correct */
|
|
q = buf;
|
|
|
|
/* Exponent handling: if exponent format is used, record exponent value and
|
|
* fake k such that one leading digit is generated (e.g. digits=123 -> "1.23").
|
|
*
|
|
* toFixed() prevents exponent use; otherwise apply a set of criteria to
|
|
* match the other API calls (toString(), toPrecision, etc).
|
|
*/
|
|
|
|
expt = DUK__NO_EXP;
|
|
if (!nc_ctx->abs_pos /* toFixed() */) {
|
|
if ((flags & DUK_N2S_FLAG_FORCE_EXP) || /* exponential notation forced */
|
|
((flags & DUK_N2S_FLAG_NO_ZERO_PAD) && /* fixed precision and zero padding would be required */
|
|
(k - digits >= 1)) || /* (e.g. k=3, digits=2 -> "12X") */
|
|
((k > 21 || k <= -6) && (radix == 10))) { /* toString() conditions */
|
|
DUK_DDD(DUK_DDDPRINT("use exponential notation: k=%ld -> expt=%ld",
|
|
(long) k, (long) (k - 1)));
|
|
expt = k - 1; /* e.g. 12.3 -> digits="123" k=2 -> 1.23e1 */
|
|
k = 1; /* generate mantissa with a single leading whole number digit */
|
|
}
|
|
}
|
|
|
|
if (neg) {
|
|
*q++ = '-';
|
|
}
|
|
|
|
/* Start position (inclusive) and end position (exclusive) */
|
|
pos = (k >= 1 ? k : 1);
|
|
if (nc_ctx->is_fixed) {
|
|
if (nc_ctx->abs_pos) {
|
|
/* toFixed() */
|
|
pos_end = -digits;
|
|
} else {
|
|
pos_end = k - digits;
|
|
}
|
|
} else {
|
|
pos_end = k - nc_ctx->count;
|
|
}
|
|
if (pos_end > 0) {
|
|
pos_end = 0;
|
|
}
|
|
|
|
DUK_DDD(DUK_DDDPRINT("expt=%ld, k=%ld, count=%ld, pos=%ld, pos_end=%ld, is_fixed=%ld, "
|
|
"digits=%ld, abs_pos=%ld",
|
|
(long) expt, (long) k, (long) nc_ctx->count, (long) pos, (long) pos_end,
|
|
(long) nc_ctx->is_fixed, (long) digits, (long) nc_ctx->abs_pos));
|
|
|
|
/* Digit generation */
|
|
while (pos > pos_end) {
|
|
DUK_DDD(DUK_DDDPRINT("digit generation: pos=%ld, pos_end=%ld",
|
|
(long) pos, (long) pos_end));
|
|
if (pos == 0) {
|
|
*q++ = (duk_uint8_t) '.';
|
|
}
|
|
if (pos > k) {
|
|
*q++ = (duk_uint8_t) '0';
|
|
} else if (pos <= k - nc_ctx->count) {
|
|
*q++ = (duk_uint8_t) '0';
|
|
} else {
|
|
dig = nc_ctx->digits[k - pos];
|
|
DUK_ASSERT(dig >= 0 && dig < nc_ctx->B);
|
|
*q++ = (duk_uint8_t) DUK__DIGITCHAR(dig);
|
|
}
|
|
|
|
pos--;
|
|
}
|
|
DUK_ASSERT(pos <= 1);
|
|
|
|
/* Exponent */
|
|
if (expt != DUK__NO_EXP) {
|
|
/*
|
|
* Exponent notation for non-base-10 numbers isn't specified in ECMAScript
|
|
* specification, as it never explicitly turns up: non-decimal numbers can
|
|
* only be formatted with Number.prototype.toString([radix]) and for that,
|
|
* behavior is not explicitly specified.
|
|
*
|
|
* Logical choices include formatting the exponent as decimal (e.g. binary
|
|
* 100000 as 1e+5) or in current radix (e.g. binary 100000 as 1e+101).
|
|
* The Dragon4 algorithm (in the original paper) prints the exponent value
|
|
* in the target radix B. However, for radix values 15 and above, the
|
|
* exponent separator 'e' is no longer easily parseable. Consider, for
|
|
* instance, the number "1.faecee+1c".
|
|
*/
|
|
|
|
duk_size_t len;
|
|
char expt_sign;
|
|
|
|
*q++ = 'e';
|
|
if (expt >= 0) {
|
|
expt_sign = '+';
|
|
} else {
|
|
expt_sign = '-';
|
|
expt = -expt;
|
|
}
|
|
*q++ = (duk_uint8_t) expt_sign;
|
|
len = duk__dragon4_format_uint32(q, (duk_uint32_t) expt, radix);
|
|
q += len;
|
|
}
|
|
|
|
duk_push_lstring(thr, (const char *) buf, (size_t) (q - buf));
|
|
}
|
|
|
|
/*
|
|
* Conversion helpers
|
|
*/
|
|
|
|
DUK_LOCAL void duk__dragon4_double_to_ctx(duk__numconv_stringify_ctx *nc_ctx, duk_double_t x) {
|
|
duk_double_union u;
|
|
duk_uint32_t tmp;
|
|
duk_small_int_t expt;
|
|
|
|
/*
|
|
* seeeeeee eeeeffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff
|
|
* A B C D E F G H
|
|
*
|
|
* s sign bit
|
|
* eee... exponent field
|
|
* fff... fraction
|
|
*
|
|
* ieee value = 1.ffff... * 2^(e - 1023) (normal)
|
|
* = 0.ffff... * 2^(-1022) (denormal)
|
|
*
|
|
* algorithm v = f * b^e
|
|
*/
|
|
|
|
DUK_DBLUNION_SET_DOUBLE(&u, x);
|
|
|
|
nc_ctx->f.n = 2;
|
|
|
|
tmp = DUK_DBLUNION_GET_LOW32(&u);
|
|
nc_ctx->f.v[0] = tmp;
|
|
tmp = DUK_DBLUNION_GET_HIGH32(&u);
|
|
nc_ctx->f.v[1] = tmp & 0x000fffffUL;
|
|
expt = (duk_small_int_t) ((tmp >> 20) & 0x07ffUL);
|
|
|
|
if (expt == 0) {
|
|
/* denormal */
|
|
expt = DUK__IEEE_DOUBLE_EXP_MIN - 52;
|
|
duk__bi_normalize(&nc_ctx->f);
|
|
} else {
|
|
/* normal: implicit leading 1-bit */
|
|
nc_ctx->f.v[1] |= 0x00100000UL;
|
|
expt = expt - DUK__IEEE_DOUBLE_EXP_BIAS - 52;
|
|
DUK_ASSERT(duk__bi_is_valid(&nc_ctx->f)); /* true, because v[1] has at least one bit set */
|
|
}
|
|
|
|
DUK_ASSERT(duk__bi_is_valid(&nc_ctx->f));
|
|
|
|
nc_ctx->e = expt;
|
|
}
|
|
|
|
DUK_LOCAL void duk__dragon4_ctx_to_double(duk__numconv_stringify_ctx *nc_ctx, duk_double_t *x) {
|
|
duk_double_union u;
|
|
duk_small_int_t expt;
|
|
duk_small_int_t i;
|
|
duk_small_int_t bitstart;
|
|
duk_small_int_t bitround;
|
|
duk_small_int_t bitidx;
|
|
duk_small_int_t skip_round;
|
|
duk_uint32_t t, v;
|
|
|
|
DUK_ASSERT(nc_ctx->count == 53 + 1);
|
|
|
|
/* Sometimes this assert is not true right now; it will be true after
|
|
* rounding. See: test-bug-numconv-mantissa-assert.js.
|
|
*/
|
|
DUK_ASSERT_DISABLE(nc_ctx->digits[0] == 1); /* zero handled by caller */
|
|
|
|
/* Should not be required because the code below always sets both high
|
|
* and low parts, but at least gcc-4.4.5 fails to deduce this correctly
|
|
* (perhaps because the low part is set (seemingly) conditionally in a
|
|
* loop), so this is here to avoid the bogus warning.
|
|
*/
|
|
duk_memzero((void *) &u, sizeof(u));
|
|
|
|
/*
|
|
* Figure out how generated digits match up with the mantissa,
|
|
* and then perform rounding. If mantissa overflows, need to
|
|
* recompute the exponent (it is bumped and may overflow to
|
|
* infinity).
|
|
*
|
|
* For normal numbers the leading '1' is hidden and ignored,
|
|
* and the last bit is used for rounding:
|
|
*
|
|
* rounding pt
|
|
* <--------52------->|
|
|
* 1 x x x x ... x x x x|y ==> x x x x ... x x x x
|
|
*
|
|
* For denormals, the leading '1' is included in the number,
|
|
* and the rounding point is different:
|
|
*
|
|
* rounding pt
|
|
* <--52 or less--->|
|
|
* 1 x x x x ... x x|x x y ==> 0 0 ... 1 x x ... x x
|
|
*
|
|
* The largest denormals will have a mantissa beginning with
|
|
* a '1' (the explicit leading bit); smaller denormals will
|
|
* have leading zero bits.
|
|
*
|
|
* If the exponent would become too high, the result becomes
|
|
* Infinity. If the exponent is so small that the entire
|
|
* mantissa becomes zero, the result becomes zero.
|
|
*
|
|
* Note: the Dragon4 'k' is off-by-one with respect to the IEEE
|
|
* exponent. For instance, k==0 indicates that the leading '1'
|
|
* digit is at the first binary fraction position (0.1xxx...);
|
|
* the corresponding IEEE exponent would be -1.
|
|
*/
|
|
|
|
skip_round = 0;
|
|
|
|
recheck_exp:
|
|
|
|
expt = nc_ctx->k - 1; /* IEEE exp without bias */
|
|
if (expt > 1023) {
|
|
/* Infinity */
|
|
bitstart = -255; /* needed for inf: causes mantissa to become zero,
|
|
* and rounding to be skipped.
|
|
*/
|
|
expt = 2047;
|
|
} else if (expt >= -1022) {
|
|
/* normal */
|
|
bitstart = 1; /* skip leading digit */
|
|
expt += DUK__IEEE_DOUBLE_EXP_BIAS;
|
|
DUK_ASSERT(expt >= 1 && expt <= 2046);
|
|
} else {
|
|
/* denormal or zero */
|
|
bitstart = 1023 + expt; /* expt==-1023 -> bitstart=0 (leading 1);
|
|
* expt==-1024 -> bitstart=-1 (one left of leading 1), etc
|
|
*/
|
|
expt = 0;
|
|
}
|
|
bitround = bitstart + 52;
|
|
|
|
DUK_DDD(DUK_DDDPRINT("ieee expt=%ld, bitstart=%ld, bitround=%ld",
|
|
(long) expt, (long) bitstart, (long) bitround));
|
|
|
|
if (!skip_round) {
|
|
if (duk__dragon4_fixed_format_round(nc_ctx, bitround)) {
|
|
/* Corner case: see test-numconv-parse-mant-carry.js. We could
|
|
* just bump the exponent and update bitstart, but it's more robust
|
|
* to recompute (but avoid rounding twice).
|
|
*/
|
|
DUK_DDD(DUK_DDDPRINT("rounding caused exponent to be bumped, recheck exponent"));
|
|
skip_round = 1;
|
|
goto recheck_exp;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Create mantissa
|
|
*/
|
|
|
|
t = 0;
|
|
for (i = 0; i < 52; i++) {
|
|
bitidx = bitstart + 52 - 1 - i;
|
|
if (bitidx >= nc_ctx->count) {
|
|
v = 0;
|
|
} else if (bitidx < 0) {
|
|
v = 0;
|
|
} else {
|
|
v = nc_ctx->digits[bitidx];
|
|
}
|
|
DUK_ASSERT(v == 0 || v == 1);
|
|
t += v << (i % 32);
|
|
if (i == 31) {
|
|
/* low 32 bits is complete */
|
|
DUK_DBLUNION_SET_LOW32(&u, t);
|
|
t = 0;
|
|
}
|
|
}
|
|
/* t has high mantissa */
|
|
|
|
DUK_DDD(DUK_DDDPRINT("mantissa is complete: %08lx %08lx",
|
|
(unsigned long) t,
|
|
(unsigned long) DUK_DBLUNION_GET_LOW32(&u)));
|
|
|
|
DUK_ASSERT(expt >= 0 && expt <= 0x7ffL);
|
|
t += ((duk_uint32_t) expt) << 20;
|
|
#if 0 /* caller handles sign change */
|
|
if (negative) {
|
|
t |= 0x80000000U;
|
|
}
|
|
#endif
|
|
DUK_DBLUNION_SET_HIGH32(&u, t);
|
|
|
|
DUK_DDD(DUK_DDDPRINT("number is complete: %08lx %08lx",
|
|
(unsigned long) DUK_DBLUNION_GET_HIGH32(&u),
|
|
(unsigned long) DUK_DBLUNION_GET_LOW32(&u)));
|
|
|
|
*x = DUK_DBLUNION_GET_DOUBLE(&u);
|
|
}
|
|
|
|
/*
|
|
* Exposed number-to-string API
|
|
*
|
|
* Input: [ number ]
|
|
* Output: [ string ]
|
|
*/
|
|
|
|
DUK_LOCAL DUK_NOINLINE void duk__numconv_stringify_raw(duk_hthread *thr, duk_small_int_t radix, duk_small_int_t digits, duk_small_uint_t flags) {
|
|
duk_double_t x;
|
|
duk_small_int_t c;
|
|
duk_small_int_t neg;
|
|
duk_uint32_t uval;
|
|
duk__numconv_stringify_ctx nc_ctx_alloc; /* large context; around 2kB now */
|
|
duk__numconv_stringify_ctx *nc_ctx = &nc_ctx_alloc;
|
|
|
|
x = (duk_double_t) duk_require_number(thr, -1);
|
|
duk_pop(thr);
|
|
|
|
/*
|
|
* Handle special cases (NaN, infinity, zero).
|
|
*/
|
|
|
|
c = (duk_small_int_t) DUK_FPCLASSIFY(x);
|
|
if (DUK_SIGNBIT((double) x)) {
|
|
x = -x;
|
|
neg = 1;
|
|
} else {
|
|
neg = 0;
|
|
}
|
|
|
|
/* NaN sign bit is platform specific with unpacked, un-normalized NaNs */
|
|
DUK_ASSERT(c == DUK_FP_NAN || DUK_SIGNBIT((double) x) == 0);
|
|
|
|
if (c == DUK_FP_NAN) {
|
|
duk_push_hstring_stridx(thr, DUK_STRIDX_NAN);
|
|
return;
|
|
} else if (c == DUK_FP_INFINITE) {
|
|
if (neg) {
|
|
/* -Infinity */
|
|
duk_push_hstring_stridx(thr, DUK_STRIDX_MINUS_INFINITY);
|
|
} else {
|
|
/* Infinity */
|
|
duk_push_hstring_stridx(thr, DUK_STRIDX_INFINITY);
|
|
}
|
|
return;
|
|
} else if (c == DUK_FP_ZERO) {
|
|
/* We can't shortcut zero here if it goes through special formatting
|
|
* (such as forced exponential notation).
|
|
*/
|
|
;
|
|
}
|
|
|
|
/*
|
|
* Handle integers in 32-bit range (that is, [-(2**32-1),2**32-1])
|
|
* specially, as they're very likely for embedded programs. This
|
|
* is now done for all radix values. We must be careful not to use
|
|
* the fast path when special formatting (e.g. forced exponential)
|
|
* is in force.
|
|
*
|
|
* XXX: could save space by supporting radix 10 only and using
|
|
* sprintf "%lu" for the fast path and for exponent formatting.
|
|
*/
|
|
|
|
uval = duk_double_to_uint32_t(x);
|
|
if (duk_double_equals((double) uval, x) && /* integer number in range */
|
|
flags == 0) { /* no special formatting */
|
|
/* use bigint area as a temp */
|
|
duk_uint8_t *buf = (duk_uint8_t *) (&nc_ctx->f);
|
|
duk_uint8_t *p = buf;
|
|
|
|
DUK_ASSERT(DUK__NUMCONV_CTX_BIGINTS_SIZE >= 32 + 1); /* max size: radix=2 + sign */
|
|
if (neg && uval != 0) {
|
|
/* no negative sign for zero */
|
|
*p++ = (duk_uint8_t) '-';
|
|
}
|
|
p += duk__dragon4_format_uint32(p, uval, radix);
|
|
duk_push_lstring(thr, (const char *) buf, (duk_size_t) (p - buf));
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Dragon4 setup.
|
|
*
|
|
* Convert double from IEEE representation for conversion;
|
|
* normal finite values have an implicit leading 1-bit. The
|
|
* slow path algorithm doesn't handle zero, so zero is special
|
|
* cased here but still creates a valid nc_ctx, and goes
|
|
* through normal formatting in case special formatting has
|
|
* been requested (e.g. forced exponential format: 0 -> "0e+0").
|
|
*/
|
|
|
|
/* Would be nice to bulk clear the allocation, but the context
|
|
* is 1-2 kilobytes and nothing should rely on it being zeroed.
|
|
*/
|
|
#if 0
|
|
duk_memzero((void *) nc_ctx, sizeof(*nc_ctx)); /* slow init, do only for slow path cases */
|
|
#endif
|
|
|
|
nc_ctx->is_s2n = 0;
|
|
nc_ctx->b = 2;
|
|
nc_ctx->B = radix;
|
|
nc_ctx->abs_pos = 0;
|
|
if (flags & DUK_N2S_FLAG_FIXED_FORMAT) {
|
|
nc_ctx->is_fixed = 1;
|
|
if (flags & DUK_N2S_FLAG_FRACTION_DIGITS) {
|
|
/* absolute req_digits; e.g. digits = 1 -> last digit is 0,
|
|
* but add an extra digit for rounding.
|
|
*/
|
|
nc_ctx->abs_pos = 1;
|
|
nc_ctx->req_digits = (-digits + 1) - 1;
|
|
} else {
|
|
nc_ctx->req_digits = digits + 1;
|
|
}
|
|
} else {
|
|
nc_ctx->is_fixed = 0;
|
|
nc_ctx->req_digits = 0;
|
|
}
|
|
|
|
if (c == DUK_FP_ZERO) {
|
|
/* Zero special case: fake requested number of zero digits; ensure
|
|
* no sign bit is printed. Relative and absolute fixed format
|
|
* require separate handling.
|
|
*/
|
|
duk_small_int_t count;
|
|
if (nc_ctx->is_fixed) {
|
|
if (nc_ctx->abs_pos) {
|
|
count = digits + 2; /* lead zero + 'digits' fractions + 1 for rounding */
|
|
} else {
|
|
count = digits + 1; /* + 1 for rounding */
|
|
}
|
|
} else {
|
|
count = 1;
|
|
}
|
|
DUK_DDD(DUK_DDDPRINT("count=%ld", (long) count));
|
|
DUK_ASSERT(count >= 1);
|
|
duk_memzero((void *) nc_ctx->digits, (size_t) count);
|
|
nc_ctx->count = count;
|
|
nc_ctx->k = 1; /* 0.000... */
|
|
neg = 0;
|
|
goto zero_skip;
|
|
}
|
|
|
|
duk__dragon4_double_to_ctx(nc_ctx, x); /* -> sets 'f' and 'e' */
|
|
DUK__BI_PRINT("f", &nc_ctx->f);
|
|
DUK_DDD(DUK_DDDPRINT("e=%ld", (long) nc_ctx->e));
|
|
|
|
/*
|
|
* Dragon4 slow path digit generation.
|
|
*/
|
|
|
|
duk__dragon4_prepare(nc_ctx); /* setup many variables in nc_ctx */
|
|
|
|
DUK_DDD(DUK_DDDPRINT("after prepare:"));
|
|
DUK__BI_PRINT("r", &nc_ctx->r);
|
|
DUK__BI_PRINT("s", &nc_ctx->s);
|
|
DUK__BI_PRINT("mp", &nc_ctx->mp);
|
|
DUK__BI_PRINT("mm", &nc_ctx->mm);
|
|
|
|
duk__dragon4_scale(nc_ctx);
|
|
|
|
DUK_DDD(DUK_DDDPRINT("after scale; k=%ld", (long) nc_ctx->k));
|
|
DUK__BI_PRINT("r", &nc_ctx->r);
|
|
DUK__BI_PRINT("s", &nc_ctx->s);
|
|
DUK__BI_PRINT("mp", &nc_ctx->mp);
|
|
DUK__BI_PRINT("mm", &nc_ctx->mm);
|
|
|
|
duk__dragon4_generate(nc_ctx);
|
|
|
|
/*
|
|
* Convert and push final string.
|
|
*/
|
|
|
|
zero_skip:
|
|
|
|
if (flags & DUK_N2S_FLAG_FIXED_FORMAT) {
|
|
/* Perform fixed-format rounding. */
|
|
duk_small_int_t roundpos;
|
|
if (flags & DUK_N2S_FLAG_FRACTION_DIGITS) {
|
|
/* 'roundpos' is relative to nc_ctx->k and increases to the right
|
|
* (opposite of how 'k' changes).
|
|
*/
|
|
roundpos = -digits; /* absolute position for digit considered for rounding */
|
|
roundpos = nc_ctx->k - roundpos;
|
|
} else {
|
|
roundpos = digits;
|
|
}
|
|
DUK_DDD(DUK_DDDPRINT("rounding: k=%ld, count=%ld, digits=%ld, roundpos=%ld",
|
|
(long) nc_ctx->k, (long) nc_ctx->count, (long) digits, (long) roundpos));
|
|
(void) duk__dragon4_fixed_format_round(nc_ctx, roundpos);
|
|
|
|
/* Note: 'count' is currently not adjusted by rounding (i.e. the
|
|
* digits are not "chopped off". That shouldn't matter because
|
|
* the digit position (absolute or relative) is passed on to the
|
|
* convert-and-push function.
|
|
*/
|
|
}
|
|
|
|
duk__dragon4_convert_and_push(nc_ctx, thr, radix, digits, flags, neg);
|
|
}
|
|
|
|
DUK_INTERNAL void duk_numconv_stringify(duk_hthread *thr, duk_small_int_t radix, duk_small_int_t digits, duk_small_uint_t flags) {
|
|
duk_native_stack_check(thr);
|
|
duk__numconv_stringify_raw(thr, radix, digits, flags);
|
|
}
|
|
|
|
/*
|
|
* Exposed string-to-number API
|
|
*
|
|
* Input: [ string ]
|
|
* Output: [ number ]
|
|
*
|
|
* If number parsing fails, a NaN is pushed as the result. If number parsing
|
|
* fails due to an internal error, an InternalError is thrown.
|
|
*/
|
|
|
|
DUK_LOCAL DUK_NOINLINE void duk__numconv_parse_raw(duk_hthread *thr, duk_small_int_t radix, duk_small_uint_t flags) {
|
|
duk__numconv_stringify_ctx nc_ctx_alloc; /* large context; around 2kB now */
|
|
duk__numconv_stringify_ctx *nc_ctx = &nc_ctx_alloc;
|
|
duk_double_t res;
|
|
duk_hstring *h_str;
|
|
duk_int_t expt;
|
|
duk_bool_t expt_neg;
|
|
duk_small_int_t expt_adj;
|
|
duk_small_int_t neg;
|
|
duk_small_int_t dig;
|
|
duk_small_int_t dig_whole;
|
|
duk_small_int_t dig_lzero;
|
|
duk_small_int_t dig_frac;
|
|
duk_small_int_t dig_expt;
|
|
duk_small_int_t dig_prec;
|
|
const duk__exp_limits *explim;
|
|
const duk_uint8_t *p;
|
|
duk_small_int_t ch;
|
|
|
|
DUK_DDD(DUK_DDDPRINT("parse number: %!T, radix=%ld, flags=0x%08lx",
|
|
(duk_tval *) duk_get_tval(thr, -1),
|
|
(long) radix, (unsigned long) flags));
|
|
|
|
DUK_ASSERT(radix >= 2 && radix <= 36);
|
|
DUK_ASSERT(radix - 2 < (duk_small_int_t) sizeof(duk__str2num_digits_for_radix));
|
|
|
|
/*
|
|
* Preliminaries: trim, sign, Infinity check
|
|
*
|
|
* We rely on the interned string having a NUL terminator, which will
|
|
* cause a parse failure wherever it is encountered. As a result, we
|
|
* don't need separate pointer checks.
|
|
*
|
|
* There is no special parsing for 'NaN' in the specification although
|
|
* 'Infinity' (with an optional sign) is allowed in some contexts.
|
|
* Some contexts allow plus/minus sign, while others only allow the
|
|
* minus sign (like JSON.parse()).
|
|
*
|
|
* Automatic hex number detection (leading '0x' or '0X') and octal
|
|
* number detection (leading '0' followed by at least one octal digit)
|
|
* is done here too.
|
|
*
|
|
* Symbols are not explicitly rejected here (that's up to the caller).
|
|
* If a symbol were passed here, it should ultimately safely fail
|
|
* parsing due to a syntax error.
|
|
*/
|
|
|
|
if (flags & DUK_S2N_FLAG_TRIM_WHITE) {
|
|
/* Leading / trailing whitespace is sometimes accepted and
|
|
* sometimes not. After white space trimming, all valid input
|
|
* characters are pure ASCII.
|
|
*/
|
|
duk_trim(thr, -1);
|
|
}
|
|
h_str = duk_require_hstring(thr, -1);
|
|
DUK_ASSERT(h_str != NULL);
|
|
p = (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h_str);
|
|
|
|
neg = 0;
|
|
ch = *p;
|
|
if (ch == (duk_small_int_t) '+') {
|
|
if ((flags & DUK_S2N_FLAG_ALLOW_PLUS) == 0) {
|
|
DUK_DDD(DUK_DDDPRINT("parse failed: leading plus sign not allowed"));
|
|
goto parse_fail;
|
|
}
|
|
p++;
|
|
} else if (ch == (duk_small_int_t) '-') {
|
|
if ((flags & DUK_S2N_FLAG_ALLOW_MINUS) == 0) {
|
|
DUK_DDD(DUK_DDDPRINT("parse failed: leading minus sign not allowed"));
|
|
goto parse_fail;
|
|
}
|
|
p++;
|
|
neg = 1;
|
|
}
|
|
|
|
if ((flags & DUK_S2N_FLAG_ALLOW_INF) && DUK_STRNCMP((const char *) p, "Infinity", 8) == 0) {
|
|
/* Don't check for Infinity unless the context allows it.
|
|
* 'Infinity' is a valid integer literal in e.g. base-36:
|
|
*
|
|
* parseInt('Infinity', 36)
|
|
* 1461559270678
|
|
*/
|
|
|
|
if ((flags & DUK_S2N_FLAG_ALLOW_GARBAGE) == 0 && p[8] != DUK_ASC_NUL) {
|
|
DUK_DDD(DUK_DDDPRINT("parse failed: trailing garbage after matching 'Infinity' not allowed"));
|
|
goto parse_fail;
|
|
} else {
|
|
res = DUK_DOUBLE_INFINITY;
|
|
goto negcheck_and_ret;
|
|
}
|
|
}
|
|
ch = *p;
|
|
if (ch == (duk_small_int_t) '0') {
|
|
duk_small_int_t detect_radix = 0;
|
|
ch = DUK_LOWERCASE_CHAR_ASCII(p[1]); /* 'x' or 'X' -> 'x' */
|
|
if ((flags & DUK_S2N_FLAG_ALLOW_AUTO_HEX_INT) && ch == DUK_ASC_LC_X) {
|
|
DUK_DDD(DUK_DDDPRINT("detected 0x/0X hex prefix, changing radix and preventing fractions and exponent"));
|
|
detect_radix = 16;
|
|
#if 0
|
|
} else if ((flags & DUK_S2N_FLAG_ALLOW_AUTO_LEGACY_OCT_INT) &&
|
|
(ch >= (duk_small_int_t) '0' && ch <= (duk_small_int_t) '9')) {
|
|
DUK_DDD(DUK_DDDPRINT("detected 0n oct prefix, changing radix and preventing fractions and exponent"));
|
|
detect_radix = 8;
|
|
|
|
/* NOTE: if this legacy octal case is added back, it has
|
|
* different flags and 'p' advance so this needs to be
|
|
* reworked.
|
|
*/
|
|
flags |= DUK_S2N_FLAG_ALLOW_EMPTY_AS_ZERO; /* interpret e.g. '09' as '0', not NaN */
|
|
p += 1;
|
|
#endif
|
|
} else if ((flags & DUK_S2N_FLAG_ALLOW_AUTO_OCT_INT) && ch == DUK_ASC_LC_O) {
|
|
DUK_DDD(DUK_DDDPRINT("detected 0o oct prefix, changing radix and preventing fractions and exponent"));
|
|
detect_radix = 8;
|
|
} else if ((flags & DUK_S2N_FLAG_ALLOW_AUTO_BIN_INT) && ch == DUK_ASC_LC_B) {
|
|
DUK_DDD(DUK_DDDPRINT("detected 0b bin prefix, changing radix and preventing fractions and exponent"));
|
|
detect_radix = 2;
|
|
}
|
|
if (detect_radix > 0) {
|
|
radix = detect_radix;
|
|
/* Clear empty as zero flag: interpret e.g. '0x' and '0xg' as a NaN (= parse error) */
|
|
flags &= ~(DUK_S2N_FLAG_ALLOW_EXP | DUK_S2N_FLAG_ALLOW_EMPTY_FRAC |
|
|
DUK_S2N_FLAG_ALLOW_FRAC | DUK_S2N_FLAG_ALLOW_NAKED_FRAC |
|
|
DUK_S2N_FLAG_ALLOW_EMPTY_AS_ZERO);
|
|
flags |= DUK_S2N_FLAG_ALLOW_LEADING_ZERO; /* allow e.g. '0x0009' and '0b00010001' */
|
|
p += 2;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Scan number and setup for Dragon4.
|
|
*
|
|
* The fast path case is detected during setup: an integer which
|
|
* can be converted without rounding, no net exponent. The fast
|
|
* path could be implemented as a separate scan, but may not really
|
|
* be worth it: the multiplications for building 'f' are not
|
|
* expensive when 'f' is small.
|
|
*
|
|
* The significand ('f') must contain enough bits of (apparent)
|
|
* accuracy, so that Dragon4 will generate enough binary output digits.
|
|
* For decimal numbers, this means generating a 20-digit significand,
|
|
* which should yield enough practical accuracy to parse IEEE doubles.
|
|
* In fact, the ECMAScript specification explicitly allows an
|
|
* implementation to treat digits beyond 20 as zeroes (and even
|
|
* to round the 20th digit upwards). For non-decimal numbers, the
|
|
* appropriate number of digits has been precomputed for comparable
|
|
* accuracy.
|
|
*
|
|
* Digit counts:
|
|
*
|
|
* [ dig_lzero ]
|
|
* |
|
|
* .+-..---[ dig_prec ]----.
|
|
* | || |
|
|
* 0000123.456789012345678901234567890e+123456
|
|
* | | | | | |
|
|
* `--+--' `------[ dig_frac ]-------' `-+--'
|
|
* | |
|
|
* [ dig_whole ] [ dig_expt ]
|
|
*
|
|
* dig_frac and dig_expt are -1 if not present
|
|
* dig_lzero is only computed for whole number part
|
|
*
|
|
* Parsing state
|
|
*
|
|
* Parsing whole part dig_frac < 0 AND dig_expt < 0
|
|
* Parsing fraction part dig_frac >= 0 AND dig_expt < 0
|
|
* Parsing exponent part dig_expt >= 0 (dig_frac may be < 0 or >= 0)
|
|
*
|
|
* Note: in case we hit an implementation limit (like exponent range),
|
|
* we should throw an error, NOT return NaN or Infinity. Even with
|
|
* very large exponent (or significand) values the final result may be
|
|
* finite, so NaN/Infinity would be incorrect.
|
|
*/
|
|
|
|
duk__bi_set_small(&nc_ctx->f, 0);
|
|
dig_prec = 0;
|
|
dig_lzero = 0;
|
|
dig_whole = 0;
|
|
dig_frac = -1;
|
|
dig_expt = -1;
|
|
expt = 0;
|
|
expt_adj = 0; /* essentially tracks digit position of lowest 'f' digit */
|
|
expt_neg = 0;
|
|
for (;;) {
|
|
ch = *p++;
|
|
|
|
DUK_DDD(DUK_DDDPRINT("parse digits: p=%p, ch='%c' (%ld), expt=%ld, expt_adj=%ld, "
|
|
"dig_whole=%ld, dig_frac=%ld, dig_expt=%ld, dig_lzero=%ld, dig_prec=%ld",
|
|
(const void *) p, (int) ((ch >= 0x20 && ch <= 0x7e) ? ch : '?'), (long) ch,
|
|
(long) expt, (long) expt_adj, (long) dig_whole, (long) dig_frac,
|
|
(long) dig_expt, (long) dig_lzero, (long) dig_prec));
|
|
DUK__BI_PRINT("f", &nc_ctx->f);
|
|
|
|
/* Most common cases first. */
|
|
if (ch >= (duk_small_int_t) '0' && ch <= (duk_small_int_t) '9') {
|
|
dig = (duk_small_int_t) ch - '0' + 0;
|
|
} else if (ch == (duk_small_int_t) '.') {
|
|
/* A leading digit is not required in some cases, e.g. accept ".123".
|
|
* In other cases (JSON.parse()) a leading digit is required. This
|
|
* is checked for after the loop.
|
|
*/
|
|
if (dig_frac >= 0 || dig_expt >= 0) {
|
|
if (flags & DUK_S2N_FLAG_ALLOW_GARBAGE) {
|
|
DUK_DDD(DUK_DDDPRINT("garbage termination (invalid period)"));
|
|
break;
|
|
} else {
|
|
DUK_DDD(DUK_DDDPRINT("parse failed: period not allowed"));
|
|
goto parse_fail;
|
|
}
|
|
}
|
|
|
|
if ((flags & DUK_S2N_FLAG_ALLOW_FRAC) == 0) {
|
|
/* Some contexts don't allow fractions at all; this can't be a
|
|
* post-check because the state ('f' and expt) would be incorrect.
|
|
*/
|
|
if (flags & DUK_S2N_FLAG_ALLOW_GARBAGE) {
|
|
DUK_DDD(DUK_DDDPRINT("garbage termination (invalid first period)"));
|
|
break;
|
|
} else {
|
|
DUK_DDD(DUK_DDDPRINT("parse failed: fraction part not allowed"));
|
|
}
|
|
}
|
|
|
|
DUK_DDD(DUK_DDDPRINT("start fraction part"));
|
|
dig_frac = 0;
|
|
continue;
|
|
} else if (ch == (duk_small_int_t) 0) {
|
|
DUK_DDD(DUK_DDDPRINT("NUL termination"));
|
|
break;
|
|
} else if ((flags & DUK_S2N_FLAG_ALLOW_EXP) &&
|
|
dig_expt < 0 && (ch == (duk_small_int_t) 'e' || ch == (duk_small_int_t) 'E')) {
|
|
/* Note: we don't parse back exponent notation for anything else
|
|
* than radix 10, so this is not an ambiguous check (e.g. hex
|
|
* exponent values may have 'e' either as a significand digit
|
|
* or as an exponent separator).
|
|
*
|
|
* If the exponent separator occurs twice, 'e' will be interpreted
|
|
* as a digit (= 14) and will be rejected as an invalid decimal
|
|
* digit.
|
|
*/
|
|
|
|
DUK_DDD(DUK_DDDPRINT("start exponent part"));
|
|
|
|
/* Exponent without a sign or with a +/- sign is accepted
|
|
* by all call sites (even JSON.parse()).
|
|
*/
|
|
ch = *p;
|
|
if (ch == (duk_small_int_t) '-') {
|
|
expt_neg = 1;
|
|
p++;
|
|
} else if (ch == (duk_small_int_t) '+') {
|
|
p++;
|
|
}
|
|
dig_expt = 0;
|
|
continue;
|
|
} else if (ch >= (duk_small_int_t) 'a' && ch <= (duk_small_int_t) 'z') {
|
|
dig = (duk_small_int_t) (ch - (duk_small_int_t) 'a' + 0x0a);
|
|
} else if (ch >= (duk_small_int_t) 'A' && ch <= (duk_small_int_t) 'Z') {
|
|
dig = (duk_small_int_t) (ch - (duk_small_int_t) 'A' + 0x0a);
|
|
} else {
|
|
dig = 255; /* triggers garbage digit check below */
|
|
}
|
|
DUK_ASSERT((dig >= 0 && dig <= 35) || dig == 255);
|
|
|
|
if (dig >= radix) {
|
|
if (flags & DUK_S2N_FLAG_ALLOW_GARBAGE) {
|
|
DUK_DDD(DUK_DDDPRINT("garbage termination"));
|
|
break;
|
|
} else {
|
|
DUK_DDD(DUK_DDDPRINT("parse failed: trailing garbage or invalid digit"));
|
|
goto parse_fail;
|
|
}
|
|
}
|
|
|
|
if (dig_expt < 0) {
|
|
/* whole or fraction digit */
|
|
|
|
if (dig_prec < duk__str2num_digits_for_radix[radix - 2]) {
|
|
/* significant from precision perspective */
|
|
|
|
duk_small_int_t f_zero = duk__bi_is_zero(&nc_ctx->f);
|
|
if (f_zero && dig == 0) {
|
|
/* Leading zero is not counted towards precision digits; not
|
|
* in the integer part, nor in the fraction part.
|
|
*/
|
|
if (dig_frac < 0) {
|
|
dig_lzero++;
|
|
}
|
|
} else {
|
|
/* XXX: join these ops (multiply-accumulate), but only if
|
|
* code footprint decreases.
|
|
*/
|
|
duk__bi_mul_small(&nc_ctx->t1, &nc_ctx->f, (duk_uint32_t) radix);
|
|
duk__bi_add_small(&nc_ctx->f, &nc_ctx->t1, (duk_uint32_t) dig);
|
|
dig_prec++;
|
|
}
|
|
} else {
|
|
/* Ignore digits beyond a radix-specific limit, but note them
|
|
* in expt_adj.
|
|
*/
|
|
expt_adj++;
|
|
}
|
|
|
|
if (dig_frac >= 0) {
|
|
dig_frac++;
|
|
expt_adj--;
|
|
} else {
|
|
dig_whole++;
|
|
}
|
|
} else {
|
|
/* exponent digit */
|
|
|
|
DUK_ASSERT(radix == 10);
|
|
expt = expt * radix + dig;
|
|
if (expt > DUK_S2N_MAX_EXPONENT) {
|
|
/* Impose a reasonable exponent limit, so that exp
|
|
* doesn't need to get tracked using a bigint.
|
|
*/
|
|
DUK_DDD(DUK_DDDPRINT("parse failed: exponent too large"));
|
|
goto parse_explimit_error;
|
|
}
|
|
dig_expt++;
|
|
}
|
|
}
|
|
|
|
/* Leading zero. */
|
|
|
|
if (dig_lzero > 0 && dig_whole > 1) {
|
|
if ((flags & DUK_S2N_FLAG_ALLOW_LEADING_ZERO) == 0) {
|
|
DUK_DDD(DUK_DDDPRINT("parse failed: leading zeroes not allowed in integer part"));
|
|
goto parse_fail;
|
|
}
|
|
}
|
|
|
|
/* Validity checks for various fraction formats ("0.1", ".1", "1.", "."). */
|
|
|
|
if (dig_whole == 0) {
|
|
if (dig_frac == 0) {
|
|
/* "." is not accepted in any format */
|
|
DUK_DDD(DUK_DDDPRINT("parse failed: plain period without leading or trailing digits"));
|
|
goto parse_fail;
|
|
} else if (dig_frac > 0) {
|
|
/* ".123" */
|
|
if ((flags & DUK_S2N_FLAG_ALLOW_NAKED_FRAC) == 0) {
|
|
DUK_DDD(DUK_DDDPRINT("parse failed: fraction part not allowed without "
|
|
"leading integer digit(s)"));
|
|
goto parse_fail;
|
|
}
|
|
} else {
|
|
/* Empty ("") is allowed in some formats (e.g. Number(''), as zero,
|
|
* but it must not have a leading +/- sign (GH-2019). Note that
|
|
* for Number(), h_str is already trimmed so we can check for zero
|
|
* length and still get Number(' + ') == NaN.
|
|
*/
|
|
if ((flags & DUK_S2N_FLAG_ALLOW_EMPTY_AS_ZERO) == 0) {
|
|
DUK_DDD(DUK_DDDPRINT("parse failed: empty string not allowed (as zero)"));
|
|
goto parse_fail;
|
|
} else if (DUK_HSTRING_GET_BYTELEN(h_str) != 0) {
|
|
DUK_DDD(DUK_DDDPRINT("parse failed: no digits, but not empty (had a +/- sign)"));
|
|
goto parse_fail;
|
|
}
|
|
}
|
|
} else {
|
|
if (dig_frac == 0) {
|
|
/* "123." is allowed in some formats */
|
|
if ((flags & DUK_S2N_FLAG_ALLOW_EMPTY_FRAC) == 0) {
|
|
DUK_DDD(DUK_DDDPRINT("parse failed: empty fractions"));
|
|
goto parse_fail;
|
|
}
|
|
} else if (dig_frac > 0) {
|
|
/* "123.456" */
|
|
;
|
|
} else {
|
|
/* "123" */
|
|
;
|
|
}
|
|
}
|
|
|
|
/* Exponent without digits (e.g. "1e" or "1e+"). If trailing garbage is
|
|
* allowed, ignore exponent part as garbage (= parse as "1", i.e. exp 0).
|
|
*/
|
|
|
|
if (dig_expt == 0) {
|
|
if ((flags & DUK_S2N_FLAG_ALLOW_GARBAGE) == 0) {
|
|
DUK_DDD(DUK_DDDPRINT("parse failed: empty exponent"));
|
|
goto parse_fail;
|
|
}
|
|
DUK_ASSERT(expt == 0);
|
|
}
|
|
|
|
if (expt_neg) {
|
|
expt = -expt;
|
|
}
|
|
DUK_DDD(DUK_DDDPRINT("expt=%ld, expt_adj=%ld, net exponent -> %ld",
|
|
(long) expt, (long) expt_adj, (long) (expt + expt_adj)));
|
|
expt += expt_adj;
|
|
|
|
/* Fast path check. */
|
|
|
|
if (nc_ctx->f.n <= 1 && /* 32-bit value */
|
|
expt == 0 /* no net exponent */) {
|
|
/* Fast path is triggered for no exponent and also for balanced exponent
|
|
* and fraction parts, e.g. for "1.23e2" == "123". Remember to respect
|
|
* zero sign.
|
|
*/
|
|
|
|
/* XXX: could accept numbers larger than 32 bits, e.g. up to 53 bits? */
|
|
DUK_DDD(DUK_DDDPRINT("fast path number parse"));
|
|
if (nc_ctx->f.n == 1) {
|
|
res = (double) nc_ctx->f.v[0];
|
|
} else {
|
|
res = 0.0;
|
|
}
|
|
goto negcheck_and_ret;
|
|
}
|
|
|
|
/* Significand ('f') padding. */
|
|
|
|
while (dig_prec < duk__str2num_digits_for_radix[radix - 2]) {
|
|
/* Pad significand with "virtual" zero digits so that Dragon4 will
|
|
* have enough (apparent) precision to work with.
|
|
*/
|
|
DUK_DDD(DUK_DDDPRINT("dig_prec=%ld, pad significand with zero", (long) dig_prec));
|
|
duk__bi_mul_small_copy(&nc_ctx->f, (duk_uint32_t) radix, &nc_ctx->t1);
|
|
DUK__BI_PRINT("f", &nc_ctx->f);
|
|
expt--;
|
|
dig_prec++;
|
|
}
|
|
|
|
DUK_DDD(DUK_DDDPRINT("final exponent: %ld", (long) expt));
|
|
|
|
/* Detect zero special case. */
|
|
|
|
if (nc_ctx->f.n == 0) {
|
|
/* This may happen even after the fast path check, if exponent is
|
|
* not balanced (e.g. "0e1"). Remember to respect zero sign.
|
|
*/
|
|
DUK_DDD(DUK_DDDPRINT("significand is zero"));
|
|
res = 0.0;
|
|
goto negcheck_and_ret;
|
|
}
|
|
|
|
|
|
/* Quick reject of too large or too small exponents. This check
|
|
* would be incorrect for zero (e.g. "0e1000" is zero, not Infinity)
|
|
* so zero check must be above.
|
|
*/
|
|
|
|
explim = &duk__str2num_exp_limits[radix - 2];
|
|
if (expt > explim->upper) {
|
|
DUK_DDD(DUK_DDDPRINT("exponent too large -> infinite"));
|
|
res = (duk_double_t) DUK_DOUBLE_INFINITY;
|
|
goto negcheck_and_ret;
|
|
} else if (expt < explim->lower) {
|
|
DUK_DDD(DUK_DDDPRINT("exponent too small -> zero"));
|
|
res = (duk_double_t) 0.0;
|
|
goto negcheck_and_ret;
|
|
}
|
|
|
|
nc_ctx->is_s2n = 1;
|
|
nc_ctx->e = expt;
|
|
nc_ctx->b = radix;
|
|
nc_ctx->B = 2;
|
|
nc_ctx->is_fixed = 1;
|
|
nc_ctx->abs_pos = 0;
|
|
nc_ctx->req_digits = 53 + 1;
|
|
|
|
DUK__BI_PRINT("f", &nc_ctx->f);
|
|
DUK_DDD(DUK_DDDPRINT("e=%ld", (long) nc_ctx->e));
|
|
|
|
/*
|
|
* Dragon4 slow path (binary) digit generation.
|
|
* An extra digit is generated for rounding.
|
|
*/
|
|
|
|
duk__dragon4_prepare(nc_ctx); /* setup many variables in nc_ctx */
|
|
|
|
DUK_DDD(DUK_DDDPRINT("after prepare:"));
|
|
DUK__BI_PRINT("r", &nc_ctx->r);
|
|
DUK__BI_PRINT("s", &nc_ctx->s);
|
|
DUK__BI_PRINT("mp", &nc_ctx->mp);
|
|
DUK__BI_PRINT("mm", &nc_ctx->mm);
|
|
|
|
duk__dragon4_scale(nc_ctx);
|
|
|
|
DUK_DDD(DUK_DDDPRINT("after scale; k=%ld", (long) nc_ctx->k));
|
|
DUK__BI_PRINT("r", &nc_ctx->r);
|
|
DUK__BI_PRINT("s", &nc_ctx->s);
|
|
DUK__BI_PRINT("mp", &nc_ctx->mp);
|
|
DUK__BI_PRINT("mm", &nc_ctx->mm);
|
|
|
|
duk__dragon4_generate(nc_ctx);
|
|
|
|
DUK_ASSERT(nc_ctx->count == 53 + 1);
|
|
|
|
/*
|
|
* Convert binary digits into an IEEE double. Need to handle
|
|
* denormals and rounding correctly.
|
|
*
|
|
* Some call sites currently assume the result is always a
|
|
* non-fastint double. If this is changed, check all call
|
|
* sites.
|
|
*/
|
|
|
|
duk__dragon4_ctx_to_double(nc_ctx, &res);
|
|
goto negcheck_and_ret;
|
|
|
|
negcheck_and_ret:
|
|
if (neg) {
|
|
res = -res;
|
|
}
|
|
duk_pop(thr);
|
|
duk_push_number(thr, (double) res);
|
|
DUK_DDD(DUK_DDDPRINT("result: %!T", (duk_tval *) duk_get_tval(thr, -1)));
|
|
return;
|
|
|
|
parse_fail:
|
|
DUK_DDD(DUK_DDDPRINT("parse failed"));
|
|
duk_pop(thr);
|
|
duk_push_nan(thr);
|
|
return;
|
|
|
|
parse_explimit_error:
|
|
DUK_DDD(DUK_DDDPRINT("parse failed, internal error, can't return a value"));
|
|
DUK_ERROR_RANGE(thr, "exponent too large");
|
|
DUK_WO_NORETURN(return;);
|
|
}
|
|
|
|
DUK_INTERNAL void duk_numconv_parse(duk_hthread *thr, duk_small_int_t radix, duk_small_uint_t flags) {
|
|
duk_native_stack_check(thr);
|
|
duk__numconv_parse_raw(thr, radix, flags);
|
|
}
|