Fix encoding bug in js_dtoa_radix (#399)
- fix radix conversion rounding code: incrementing the digit does not work for '9'. We can assume ASCII so it works for all other digits, especially all letters - also avoid recomputing the string length
This commit is contained in:
parent
6cb1301305
commit
f9ecc1a598
2 changed files with 34 additions and 20 deletions
51
quickjs.c
51
quickjs.c
|
@ -11261,9 +11261,10 @@ static JSValue js_dtoa(JSContext *ctx,
|
||||||
return JS_NewStringLen(ctx, buf, len);
|
return JS_NewStringLen(ctx, buf, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* d is guaranteed to be finite */
|
||||||
static JSValue js_dtoa_radix(JSContext *ctx, double d, int radix)
|
static JSValue js_dtoa_radix(JSContext *ctx, double d, int radix)
|
||||||
{
|
{
|
||||||
char buf[2200], *ptr, *ptr2;
|
char buf[2200], *ptr, *ptr2, *ptr3;
|
||||||
/* d is finite */
|
/* d is finite */
|
||||||
int sign = d < 0;
|
int sign = d < 0;
|
||||||
int digit;
|
int digit;
|
||||||
|
@ -11272,8 +11273,8 @@ static JSValue js_dtoa_radix(JSContext *ctx, double d, int radix)
|
||||||
d = fabs(d);
|
d = fabs(d);
|
||||||
d0 = trunc(d);
|
d0 = trunc(d);
|
||||||
frac = d - d0;
|
frac = d - d0;
|
||||||
ptr = buf + 1100;
|
ptr2 = buf + 1100; /* ptr2 points to the end of the string */
|
||||||
*ptr = '\0';
|
ptr = ptr2; /* ptr points to the beginning of the string */
|
||||||
if (d0 <= MAX_SAFE_INTEGER) {
|
if (d0 <= MAX_SAFE_INTEGER) {
|
||||||
int64_t n = n0 = (int64_t)d0;
|
int64_t n = n0 = (int64_t)d0;
|
||||||
while (n >= radix) {
|
while (n >= radix) {
|
||||||
|
@ -11297,7 +11298,6 @@ static JSValue js_dtoa_radix(JSContext *ctx, double d, int radix)
|
||||||
if (frac != 0) {
|
if (frac != 0) {
|
||||||
double log2_radix = log2(radix);
|
double log2_radix = log2(radix);
|
||||||
double prec = 1023 + 51; // handle subnormals
|
double prec = 1023 + 51; // handle subnormals
|
||||||
ptr2 = buf + 1100;
|
|
||||||
*ptr2++ = '.';
|
*ptr2++ = '.';
|
||||||
while (frac != 0 && n0 <= MAX_SAFE_INTEGER/2 && prec > 0) {
|
while (frac != 0 && n0 <= MAX_SAFE_INTEGER/2 && prec > 0) {
|
||||||
frac *= radix;
|
frac *= radix;
|
||||||
|
@ -11307,32 +11307,45 @@ static JSValue js_dtoa_radix(JSContext *ctx, double d, int radix)
|
||||||
n0 = n0 * radix + digit;
|
n0 = n0 * radix + digit;
|
||||||
prec -= log2_radix;
|
prec -= log2_radix;
|
||||||
}
|
}
|
||||||
*ptr2 = '\0';
|
|
||||||
if (frac * radix >= radix / 2) {
|
if (frac * radix >= radix / 2) {
|
||||||
|
/* round up the string representation manually */
|
||||||
char nine = digits36[radix - 1];
|
char nine = digits36[radix - 1];
|
||||||
// round to closest
|
while (ptr2[-1] == nine) {
|
||||||
while (ptr2[-1] == nine)
|
/* strip trailing '9' or equivalent digits */
|
||||||
*--ptr2 = '\0';
|
ptr2--;
|
||||||
if (ptr2[-1] == '.') {
|
|
||||||
*--ptr2 = '\0';
|
|
||||||
while (ptr2[-1] == nine)
|
|
||||||
*--ptr2 = '0';
|
|
||||||
}
|
}
|
||||||
if (ptr2 - 1 == ptr)
|
if (ptr2[-1] == '.') {
|
||||||
|
/* strip the 'decimal' point */
|
||||||
|
ptr2--;
|
||||||
|
/* increment the integral part */
|
||||||
|
for (ptr3 = ptr2;;) {
|
||||||
|
if (ptr3[-1] != nine) {
|
||||||
|
ptr3[-1] = (ptr3[-1] == '9') ? 'a' : ptr3[-1] + 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
*--ptr3 = '0';
|
||||||
|
if (ptr3 <= ptr) {
|
||||||
|
/* prepend a '1' if number was all nines */
|
||||||
*--ptr = '1';
|
*--ptr = '1';
|
||||||
else
|
break;
|
||||||
ptr2[-1] += 1;
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
/* increment the last fractional digit */
|
||||||
|
ptr2[-1] = (ptr2[-1] == '9') ? 'a' : ptr2[-1] + 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* strip trailing fractional zeroes */
|
||||||
while (ptr2[-1] == '0')
|
while (ptr2[-1] == '0')
|
||||||
*--ptr2 = '\0';
|
ptr2--;
|
||||||
if (ptr2[-1] == '.')
|
/* strip the 'decimal' point if last */
|
||||||
*--ptr2 = '\0';
|
ptr2 -= (ptr2[-1] == '.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
done:
|
done:
|
||||||
ptr[-1] = '-';
|
ptr[-1] = '-';
|
||||||
ptr -= sign;
|
ptr -= sign;
|
||||||
return JS_NewString(ctx, ptr);
|
return js_new_string8(ctx, (uint8_t *)ptr, ptr2 - ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
JSValue JS_ToStringInternal(JSContext *ctx, JSValue val, BOOL is_ToPropertyKey)
|
JSValue JS_ToStringInternal(JSContext *ctx, JSValue val, BOOL is_ToPropertyKey)
|
||||||
|
|
|
@ -439,6 +439,7 @@ function test_number()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert((1-2**-53).toString(12), "0.bbbbbbbbbbbbbba");
|
||||||
assert((25).toExponential(0), "3e+1");
|
assert((25).toExponential(0), "3e+1");
|
||||||
assert((-25).toExponential(0), "-3e+1");
|
assert((-25).toExponential(0), "-3e+1");
|
||||||
assert((2.5).toPrecision(1), "3");
|
assert((2.5).toPrecision(1), "3");
|
||||||
|
|
Loading…
Reference in a new issue