Improve Number.prototype.toString for radix other than 10 (#284)
- fix the conversions for integers and exact fractions - approximate approach for other cases. - bypass floating point conversions for JS_TAG_INT values - avoid divisions for base 10 integer conversions Fixes: https://github.com/quickjs-ng/quickjs/issues/242
This commit is contained in:
parent
ec4f957ca1
commit
7dd2868856
2 changed files with 98 additions and 113 deletions
107
quickjs.c
107
quickjs.c
|
@ -10971,6 +10971,8 @@ static JSValue js_bigint_to_string(JSContext *ctx, JSValue val)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 2 <= base <= 36 */
|
/* 2 <= base <= 36 */
|
||||||
|
static char const digits[36] = "0123456789abcdefghijklmnopqrstuvwxyz";
|
||||||
|
|
||||||
static char *i64toa(char *buf_end, int64_t n, unsigned int base)
|
static char *i64toa(char *buf_end, int64_t n, unsigned int base)
|
||||||
{
|
{
|
||||||
char *q = buf_end;
|
char *q = buf_end;
|
||||||
|
@ -10982,15 +10984,20 @@ static char *i64toa(char *buf_end, int64_t n, unsigned int base)
|
||||||
n = -n;
|
n = -n;
|
||||||
}
|
}
|
||||||
*--q = '\0';
|
*--q = '\0';
|
||||||
do {
|
if (base == 10) {
|
||||||
digit = (uint64_t)n % base;
|
/* division by known base uses multiplication */
|
||||||
n = (uint64_t)n / base;
|
do {
|
||||||
if (digit < 10)
|
digit = (uint64_t)n % 10;
|
||||||
digit += '0';
|
n = (uint64_t)n / 10;
|
||||||
else
|
*--q = '0' + digit;
|
||||||
digit += 'a' - 10;
|
} while (n != 0);
|
||||||
*--q = digit;
|
} else {
|
||||||
} while (n != 0);
|
do {
|
||||||
|
digit = (uint64_t)n % base;
|
||||||
|
n = (uint64_t)n / base;
|
||||||
|
*--q = digits[digit];
|
||||||
|
} while (n != 0);
|
||||||
|
}
|
||||||
if (is_neg)
|
if (is_neg)
|
||||||
*--q = '-';
|
*--q = '-';
|
||||||
return q;
|
return q;
|
||||||
|
@ -11238,6 +11245,80 @@ static JSValue js_dtoa(JSContext *ctx,
|
||||||
return JS_NewString(ctx, buf);
|
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';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
done:
|
||||||
|
ptr[-1] = '-';
|
||||||
|
ptr -= sign;
|
||||||
|
return JS_NewString(ctx, ptr);
|
||||||
|
}
|
||||||
|
|
||||||
JSValue JS_ToStringInternal(JSContext *ctx, JSValue val, BOOL is_ToPropertyKey)
|
JSValue JS_ToStringInternal(JSContext *ctx, JSValue val, BOOL is_ToPropertyKey)
|
||||||
{
|
{
|
||||||
uint32_t tag;
|
uint32_t tag;
|
||||||
|
@ -38557,8 +38638,16 @@ static JSValue js_number_toString(JSContext *ctx, JSValue this_val,
|
||||||
if (base < 0)
|
if (base < 0)
|
||||||
goto fail;
|
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);
|
||||||
|
}
|
||||||
if (JS_ToFloat64Free(ctx, &d, val))
|
if (JS_ToFloat64Free(ctx, &d, val))
|
||||||
return JS_EXCEPTION;
|
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);
|
return js_dtoa(ctx, d, base, 0, JS_DTOA_VAR_FORMAT);
|
||||||
fail:
|
fail:
|
||||||
JS_FreeValue(ctx, val);
|
JS_FreeValue(ctx, val);
|
||||||
|
|
104
v8.txt
104
v8.txt
|
@ -799,113 +799,9 @@ Failure: expected <true> found <false>
|
||||||
=== number-string-index-call.js
|
=== number-string-index-call.js
|
||||||
=== number-tostring-add.js
|
=== number-tostring-add.js
|
||||||
=== number-tostring-big-integer.js
|
=== number-tostring-big-integer.js
|
||||||
Failure:
|
|
||||||
expected:
|
|
||||||
"314404114120101444444424000000000000000"
|
|
||||||
found:
|
|
||||||
"1.2345e+27"
|
|
||||||
=== number-tostring-func.js
|
=== number-tostring-func.js
|
||||||
Failure: expected <"5a.1eb851eb852"> found <"90.12">
|
|
||||||
Failure: expected <"0.1999999999999a"> found <"0.1">
|
|
||||||
Failure: expected <"0.028f5c28f5c28f6"> found <"0.01">
|
|
||||||
Failure: expected <"0.032617c1bda511a"> found <"0.0123">
|
|
||||||
Failure: expected <"605f9f6dd18bc8000"> found <"111111111111111110000">
|
|
||||||
Failure: expected <"3c3bc3a4a2f75c0000"> found <"1.1111111111111111e+21">
|
|
||||||
Failure: expected <"25a55a46e5da9a00000"> found <"1.1111111111111111e+22">
|
|
||||||
Failure: expected <"0.0000a7c5ac471b4788"> found <"0.00001">
|
|
||||||
Failure: expected <"0.000010c6f7a0b5ed8d"> found <"0.000001">
|
|
||||||
Failure: expected <"0.000001ad7f29abcaf48"> found <"1e-7">
|
|
||||||
Failure: expected <"0.000002036565348d256"> found <"1.2e-7">
|
|
||||||
Failure: expected <"0.0000021047ee22aa466"> found <"1.23e-7">
|
|
||||||
Failure: expected <"0.0000002af31dc4611874"> found <"1e-8">
|
|
||||||
Failure: expected <"0.000000338a23b87483be"> found <"1.2e-8">
|
|
||||||
Failure: expected <"0.00000034d3fe36aaa0a2"> found <"1.23e-8">
|
|
||||||
Failure: expected <"-5a.1eb851eb852"> found <"-90.12">
|
|
||||||
Failure: expected <"-0.1999999999999a"> found <"-0.1">
|
|
||||||
Failure: expected <"-0.028f5c28f5c28f6"> found <"-0.01">
|
|
||||||
Failure: expected <"-0.032617c1bda511a"> found <"-0.0123">
|
|
||||||
Failure: expected <"-605f9f6dd18bc8000"> found <"-111111111111111110000">
|
|
||||||
Failure: expected <"-3c3bc3a4a2f75c0000"> found <"-1.1111111111111111e+21">
|
|
||||||
Failure: expected <"-25a55a46e5da9a00000"> found <"-1.1111111111111111e+22">
|
|
||||||
Failure: expected <"-0.0000a7c5ac471b4788"> found <"-0.00001">
|
|
||||||
Failure: expected <"-0.000010c6f7a0b5ed8d"> found <"-0.000001">
|
|
||||||
Failure: expected <"-0.000001ad7f29abcaf48"> found <"-1e-7">
|
|
||||||
Failure: expected <"-0.000002036565348d256"> found <"-1.2e-7">
|
|
||||||
Failure: expected <"-0.0000021047ee22aa466"> found <"-1.23e-7">
|
|
||||||
Failure: expected <"-0.0000002af31dc4611874"> found <"-1e-8">
|
|
||||||
Failure: expected <"-0.000000338a23b87483be"> found <"-1.2e-8">
|
|
||||||
Failure: expected <"-0.00000034d3fe36aaa0a2"> found <"-1.23e-8">
|
|
||||||
Failure: expected <"100000000000080"> found <"72057594037928060">
|
|
||||||
Failure: expected <"1000000000000100"> found <"1152921504606847200">
|
|
||||||
Failure: expected <"1000000000000000"> found <"1152921504606847000">
|
|
||||||
Failure: expected <"1000000000000000"> found <"1152921504606847000">
|
|
||||||
Failure:
|
|
||||||
expected:
|
|
||||||
"100000000000000000000000000000000000000000000000010000000"
|
|
||||||
found:
|
|
||||||
"72057594037928060"
|
|
||||||
Failure: expected <"-100000000000080"> found <"-72057594037928060">
|
|
||||||
Failure:
|
|
||||||
expected:
|
|
||||||
"-100000000000000000000000000000000000000000000000010000000"
|
|
||||||
found:
|
|
||||||
"-72057594037928060"
|
|
||||||
Failure: expected <"8.8"> found <"8.5">
|
|
||||||
Failure: expected <"-8.8"> found <"-8.5">
|
|
||||||
=== number-tostring-small.js
|
=== number-tostring-small.js
|
||||||
=== number-tostring.js
|
=== number-tostring.js
|
||||||
Failure: expected <"5a.1eb851eb852"> found <"90.12">
|
|
||||||
Failure: expected <"0.1999999999999a"> found <"0.1">
|
|
||||||
Failure: expected <"0.028f5c28f5c28f6"> found <"0.01">
|
|
||||||
Failure: expected <"0.032617c1bda511a"> found <"0.0123">
|
|
||||||
Failure: expected <"605f9f6dd18bc8000"> found <"111111111111111110000">
|
|
||||||
Failure: expected <"3c3bc3a4a2f75c0000"> found <"1.1111111111111111e+21">
|
|
||||||
Failure: expected <"25a55a46e5da9a00000"> found <"1.1111111111111111e+22">
|
|
||||||
Failure: expected <"0.0000a7c5ac471b4788"> found <"0.00001">
|
|
||||||
Failure: expected <"0.000010c6f7a0b5ed8d"> found <"0.000001">
|
|
||||||
Failure: expected <"0.000001ad7f29abcaf48"> found <"1e-7">
|
|
||||||
Failure: expected <"0.000002036565348d256"> found <"1.2e-7">
|
|
||||||
Failure: expected <"0.0000021047ee22aa466"> found <"1.23e-7">
|
|
||||||
Failure: expected <"0.0000002af31dc4611874"> found <"1e-8">
|
|
||||||
Failure: expected <"0.000000338a23b87483be"> found <"1.2e-8">
|
|
||||||
Failure: expected <"0.00000034d3fe36aaa0a2"> found <"1.23e-8">
|
|
||||||
Failure: expected <"-5a.1eb851eb852"> found <"-90.12">
|
|
||||||
Failure: expected <"-0.1999999999999a"> found <"-0.1">
|
|
||||||
Failure: expected <"-0.028f5c28f5c28f6"> found <"-0.01">
|
|
||||||
Failure: expected <"-0.032617c1bda511a"> found <"-0.0123">
|
|
||||||
Failure: expected <"-605f9f6dd18bc8000"> found <"-111111111111111110000">
|
|
||||||
Failure: expected <"-3c3bc3a4a2f75c0000"> found <"-1.1111111111111111e+21">
|
|
||||||
Failure: expected <"-25a55a46e5da9a00000"> found <"-1.1111111111111111e+22">
|
|
||||||
Failure: expected <"-0.0000a7c5ac471b4788"> found <"-0.00001">
|
|
||||||
Failure: expected <"-0.000010c6f7a0b5ed8d"> found <"-0.000001">
|
|
||||||
Failure: expected <"-0.000001ad7f29abcaf48"> found <"-1e-7">
|
|
||||||
Failure: expected <"-0.000002036565348d256"> found <"-1.2e-7">
|
|
||||||
Failure: expected <"-0.0000021047ee22aa466"> found <"-1.23e-7">
|
|
||||||
Failure: expected <"-0.0000002af31dc4611874"> found <"-1e-8">
|
|
||||||
Failure: expected <"-0.000000338a23b87483be"> found <"-1.2e-8">
|
|
||||||
Failure: expected <"-0.00000034d3fe36aaa0a2"> found <"-1.23e-8">
|
|
||||||
Failure: expected <"100000000000080"> found <"72057594037928060">
|
|
||||||
Failure: expected <"1000000000000100"> found <"1152921504606847200">
|
|
||||||
Failure: expected <"1000000000000000"> found <"1152921504606847000">
|
|
||||||
Failure: expected <"1000000000000000"> found <"1152921504606847000">
|
|
||||||
Failure:
|
|
||||||
expected:
|
|
||||||
"100000000000000000000000000000000000000000000000010000000"
|
|
||||||
found:
|
|
||||||
"72057594037928060"
|
|
||||||
Failure: expected <"-100000000000080"> found <"-72057594037928060">
|
|
||||||
Failure:
|
|
||||||
expected:
|
|
||||||
"-100000000000000000000000000000000000000000000000010000000"
|
|
||||||
found:
|
|
||||||
"-72057594037928060"
|
|
||||||
Failure: expected <"8.8"> found <"8.5">
|
|
||||||
Failure: expected <"-8.8"> found <"-8.5">
|
|
||||||
Failure: expected <"1.1"> found <"1.3333333333333333">
|
|
||||||
Failure: expected <"11.1"> found <"4.333333333333333">
|
|
||||||
Failure: expected <"0.01"> found <"0.1111111111111111">
|
|
||||||
Failure: expected <"10000.01"> found <"81.11111111111111">
|
|
||||||
Failure: expected <"0.0212010212010212010212010212010212"> found <"0.2857142857142857">
|
|
||||||
=== numops-fuzz-part1.js
|
=== numops-fuzz-part1.js
|
||||||
=== numops-fuzz-part2.js
|
=== numops-fuzz-part2.js
|
||||||
=== numops-fuzz-part3.js
|
=== numops-fuzz-part3.js
|
||||||
|
|
Loading…
Reference in a new issue