Skip to content

Commit 7dd2868

Browse files
authored
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: #242
1 parent ec4f957 commit 7dd2868

File tree

2 files changed

+98
-113
lines changed

2 files changed

+98
-113
lines changed

quickjs.c

Lines changed: 98 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10971,6 +10971,8 @@ static JSValue js_bigint_to_string(JSContext *ctx, JSValue val)
1097110971
}
1097210972

1097310973
/* 2 <= base <= 36 */
10974+
static char const digits[36] = "0123456789abcdefghijklmnopqrstuvwxyz";
10975+
1097410976
static char *i64toa(char *buf_end, int64_t n, unsigned int base)
1097510977
{
1097610978
char *q = buf_end;
@@ -10982,15 +10984,20 @@ static char *i64toa(char *buf_end, int64_t n, unsigned int base)
1098210984
n = -n;
1098310985
}
1098410986
*--q = '\0';
10985-
do {
10986-
digit = (uint64_t)n % base;
10987-
n = (uint64_t)n / base;
10988-
if (digit < 10)
10989-
digit += '0';
10990-
else
10991-
digit += 'a' - 10;
10992-
*--q = digit;
10993-
} while (n != 0);
10987+
if (base == 10) {
10988+
/* division by known base uses multiplication */
10989+
do {
10990+
digit = (uint64_t)n % 10;
10991+
n = (uint64_t)n / 10;
10992+
*--q = '0' + digit;
10993+
} while (n != 0);
10994+
} else {
10995+
do {
10996+
digit = (uint64_t)n % base;
10997+
n = (uint64_t)n / base;
10998+
*--q = digits[digit];
10999+
} while (n != 0);
11000+
}
1099411001
if (is_neg)
1099511002
*--q = '-';
1099611003
return q;
@@ -11238,6 +11245,80 @@ static JSValue js_dtoa(JSContext *ctx,
1123811245
return JS_NewString(ctx, buf);
1123911246
}
1124011247

11248+
static JSValue js_dtoa_radix(JSContext *ctx, double d, int radix)
11249+
{
11250+
char buf[2200], *ptr, *ptr2;
11251+
/* d is finite */
11252+
int sign = d < 0;
11253+
int digit;
11254+
double frac, d0;
11255+
int64_t n0 = 0;
11256+
d = fabs(d);
11257+
d0 = trunc(d);
11258+
frac = d - d0;
11259+
ptr = buf + 1100;
11260+
*ptr = '\0';
11261+
if (d0 <= MAX_SAFE_INTEGER) {
11262+
int64_t n = n0 = (int64_t)d0;
11263+
while (n >= radix) {
11264+
digit = n % radix;
11265+
n = n / radix;
11266+
*--ptr = digits[digit];
11267+
}
11268+
*--ptr = digits[(int)n];
11269+
} else {
11270+
/* no decimals */
11271+
while (d0 >= radix) {
11272+
digit = fmod(d0, radix);
11273+
d0 = trunc(d0 / radix);
11274+
if (d0 >= MAX_SAFE_INTEGER)
11275+
digit = 0;
11276+
*--ptr = digits[digit];
11277+
}
11278+
*--ptr = digits[(int)d0];
11279+
goto done;
11280+
}
11281+
if (frac != 0) {
11282+
double log2_radix = log2(radix);
11283+
double prec = 1023 + 51; // handle subnormals
11284+
ptr2 = buf + 1100;
11285+
*ptr2++ = '.';
11286+
while (frac != 0 && n0 <= MAX_SAFE_INTEGER/2 && prec > 0) {
11287+
frac *= radix;
11288+
digit = trunc(frac);
11289+
frac -= digit;
11290+
*ptr2++ = digits[digit];
11291+
n0 = n0 * radix + digit;
11292+
prec -= log2_radix;
11293+
}
11294+
*ptr2 = '\0';
11295+
if (frac * radix >= radix / 2) {
11296+
char nine = digits[radix - 1];
11297+
// round to closest
11298+
while (ptr2[-1] == nine)
11299+
*--ptr2 = '\0';
11300+
if (ptr2[-1] == '.') {
11301+
*--ptr2 = '\0';
11302+
while (ptr2[-1] == nine)
11303+
*--ptr2 = '0';
11304+
}
11305+
if (ptr2 - 1 == ptr)
11306+
*--ptr = '1';
11307+
else
11308+
ptr2[-1] += 1;
11309+
} else {
11310+
while (ptr2[-1] == '0')
11311+
*--ptr2 = '\0';
11312+
if (ptr2[-1] == '.')
11313+
*--ptr2 = '\0';
11314+
}
11315+
}
11316+
done:
11317+
ptr[-1] = '-';
11318+
ptr -= sign;
11319+
return JS_NewString(ctx, ptr);
11320+
}
11321+
1124111322
JSValue JS_ToStringInternal(JSContext *ctx, JSValue val, BOOL is_ToPropertyKey)
1124211323
{
1124311324
uint32_t tag;
@@ -38557,8 +38638,16 @@ static JSValue js_number_toString(JSContext *ctx, JSValue this_val,
3855738638
if (base < 0)
3855838639
goto fail;
3855938640
}
38641+
if (JS_VALUE_GET_TAG(val) == JS_TAG_INT) {
38642+
char buf1[70], *ptr;
38643+
ptr = i64toa(buf1 + sizeof(buf1), JS_VALUE_GET_INT(val), base);
38644+
return JS_NewString(ctx, ptr);
38645+
}
3856038646
if (JS_ToFloat64Free(ctx, &d, val))
3856138647
return JS_EXCEPTION;
38648+
if (base != 10 && isfinite(d)) {
38649+
return js_dtoa_radix(ctx, d, base);
38650+
}
3856238651
return js_dtoa(ctx, d, base, 0, JS_DTOA_VAR_FORMAT);
3856338652
fail:
3856438653
JS_FreeValue(ctx, val);

v8.txt

Lines changed: 0 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -799,113 +799,9 @@ Failure: expected <true> found <false>
799799
=== number-string-index-call.js
800800
=== number-tostring-add.js
801801
=== number-tostring-big-integer.js
802-
Failure:
803-
expected:
804-
"314404114120101444444424000000000000000"
805-
found:
806-
"1.2345e+27"
807802
=== number-tostring-func.js
808-
Failure: expected <"5a.1eb851eb852"> found <"90.12">
809-
Failure: expected <"0.1999999999999a"> found <"0.1">
810-
Failure: expected <"0.028f5c28f5c28f6"> found <"0.01">
811-
Failure: expected <"0.032617c1bda511a"> found <"0.0123">
812-
Failure: expected <"605f9f6dd18bc8000"> found <"111111111111111110000">
813-
Failure: expected <"3c3bc3a4a2f75c0000"> found <"1.1111111111111111e+21">
814-
Failure: expected <"25a55a46e5da9a00000"> found <"1.1111111111111111e+22">
815-
Failure: expected <"0.0000a7c5ac471b4788"> found <"0.00001">
816-
Failure: expected <"0.000010c6f7a0b5ed8d"> found <"0.000001">
817-
Failure: expected <"0.000001ad7f29abcaf48"> found <"1e-7">
818-
Failure: expected <"0.000002036565348d256"> found <"1.2e-7">
819-
Failure: expected <"0.0000021047ee22aa466"> found <"1.23e-7">
820-
Failure: expected <"0.0000002af31dc4611874"> found <"1e-8">
821-
Failure: expected <"0.000000338a23b87483be"> found <"1.2e-8">
822-
Failure: expected <"0.00000034d3fe36aaa0a2"> found <"1.23e-8">
823-
Failure: expected <"-5a.1eb851eb852"> found <"-90.12">
824-
Failure: expected <"-0.1999999999999a"> found <"-0.1">
825-
Failure: expected <"-0.028f5c28f5c28f6"> found <"-0.01">
826-
Failure: expected <"-0.032617c1bda511a"> found <"-0.0123">
827-
Failure: expected <"-605f9f6dd18bc8000"> found <"-111111111111111110000">
828-
Failure: expected <"-3c3bc3a4a2f75c0000"> found <"-1.1111111111111111e+21">
829-
Failure: expected <"-25a55a46e5da9a00000"> found <"-1.1111111111111111e+22">
830-
Failure: expected <"-0.0000a7c5ac471b4788"> found <"-0.00001">
831-
Failure: expected <"-0.000010c6f7a0b5ed8d"> found <"-0.000001">
832-
Failure: expected <"-0.000001ad7f29abcaf48"> found <"-1e-7">
833-
Failure: expected <"-0.000002036565348d256"> found <"-1.2e-7">
834-
Failure: expected <"-0.0000021047ee22aa466"> found <"-1.23e-7">
835-
Failure: expected <"-0.0000002af31dc4611874"> found <"-1e-8">
836-
Failure: expected <"-0.000000338a23b87483be"> found <"-1.2e-8">
837-
Failure: expected <"-0.00000034d3fe36aaa0a2"> found <"-1.23e-8">
838-
Failure: expected <"100000000000080"> found <"72057594037928060">
839-
Failure: expected <"1000000000000100"> found <"1152921504606847200">
840-
Failure: expected <"1000000000000000"> found <"1152921504606847000">
841-
Failure: expected <"1000000000000000"> found <"1152921504606847000">
842-
Failure:
843-
expected:
844-
"100000000000000000000000000000000000000000000000010000000"
845-
found:
846-
"72057594037928060"
847-
Failure: expected <"-100000000000080"> found <"-72057594037928060">
848-
Failure:
849-
expected:
850-
"-100000000000000000000000000000000000000000000000010000000"
851-
found:
852-
"-72057594037928060"
853-
Failure: expected <"8.8"> found <"8.5">
854-
Failure: expected <"-8.8"> found <"-8.5">
855803
=== number-tostring-small.js
856804
=== number-tostring.js
857-
Failure: expected <"5a.1eb851eb852"> found <"90.12">
858-
Failure: expected <"0.1999999999999a"> found <"0.1">
859-
Failure: expected <"0.028f5c28f5c28f6"> found <"0.01">
860-
Failure: expected <"0.032617c1bda511a"> found <"0.0123">
861-
Failure: expected <"605f9f6dd18bc8000"> found <"111111111111111110000">
862-
Failure: expected <"3c3bc3a4a2f75c0000"> found <"1.1111111111111111e+21">
863-
Failure: expected <"25a55a46e5da9a00000"> found <"1.1111111111111111e+22">
864-
Failure: expected <"0.0000a7c5ac471b4788"> found <"0.00001">
865-
Failure: expected <"0.000010c6f7a0b5ed8d"> found <"0.000001">
866-
Failure: expected <"0.000001ad7f29abcaf48"> found <"1e-7">
867-
Failure: expected <"0.000002036565348d256"> found <"1.2e-7">
868-
Failure: expected <"0.0000021047ee22aa466"> found <"1.23e-7">
869-
Failure: expected <"0.0000002af31dc4611874"> found <"1e-8">
870-
Failure: expected <"0.000000338a23b87483be"> found <"1.2e-8">
871-
Failure: expected <"0.00000034d3fe36aaa0a2"> found <"1.23e-8">
872-
Failure: expected <"-5a.1eb851eb852"> found <"-90.12">
873-
Failure: expected <"-0.1999999999999a"> found <"-0.1">
874-
Failure: expected <"-0.028f5c28f5c28f6"> found <"-0.01">
875-
Failure: expected <"-0.032617c1bda511a"> found <"-0.0123">
876-
Failure: expected <"-605f9f6dd18bc8000"> found <"-111111111111111110000">
877-
Failure: expected <"-3c3bc3a4a2f75c0000"> found <"-1.1111111111111111e+21">
878-
Failure: expected <"-25a55a46e5da9a00000"> found <"-1.1111111111111111e+22">
879-
Failure: expected <"-0.0000a7c5ac471b4788"> found <"-0.00001">
880-
Failure: expected <"-0.000010c6f7a0b5ed8d"> found <"-0.000001">
881-
Failure: expected <"-0.000001ad7f29abcaf48"> found <"-1e-7">
882-
Failure: expected <"-0.000002036565348d256"> found <"-1.2e-7">
883-
Failure: expected <"-0.0000021047ee22aa466"> found <"-1.23e-7">
884-
Failure: expected <"-0.0000002af31dc4611874"> found <"-1e-8">
885-
Failure: expected <"-0.000000338a23b87483be"> found <"-1.2e-8">
886-
Failure: expected <"-0.00000034d3fe36aaa0a2"> found <"-1.23e-8">
887-
Failure: expected <"100000000000080"> found <"72057594037928060">
888-
Failure: expected <"1000000000000100"> found <"1152921504606847200">
889-
Failure: expected <"1000000000000000"> found <"1152921504606847000">
890-
Failure: expected <"1000000000000000"> found <"1152921504606847000">
891-
Failure:
892-
expected:
893-
"100000000000000000000000000000000000000000000000010000000"
894-
found:
895-
"72057594037928060"
896-
Failure: expected <"-100000000000080"> found <"-72057594037928060">
897-
Failure:
898-
expected:
899-
"-100000000000000000000000000000000000000000000000010000000"
900-
found:
901-
"-72057594037928060"
902-
Failure: expected <"8.8"> found <"8.5">
903-
Failure: expected <"-8.8"> found <"-8.5">
904-
Failure: expected <"1.1"> found <"1.3333333333333333">
905-
Failure: expected <"11.1"> found <"4.333333333333333">
906-
Failure: expected <"0.01"> found <"0.1111111111111111">
907-
Failure: expected <"10000.01"> found <"81.11111111111111">
908-
Failure: expected <"0.0212010212010212010212010212010212"> found <"0.2857142857142857">
909805
=== numops-fuzz-part1.js
910806
=== numops-fuzz-part2.js
911807
=== numops-fuzz-part3.js

0 commit comments

Comments
 (0)