Improve Number.prototype.toString for radix other than 10
- 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
This commit is contained in:
parent
a78d2cbf7c
commit
8d64731eb8
1 changed files with 98 additions and 9 deletions
107
quickjs.c
107
quickjs.c
|
@ -11328,6 +11328,8 @@ static JSValue js_bigdecimal_to_string(JSContext *ctx, JSValueConst val)
|
|||
#endif /* CONFIG_BIGNUM */
|
||||
|
||||
/* 2 <= base <= 36 */
|
||||
static char const digits[36] = "0123456789abcdefghijklmnopqrstuvwxyz";
|
||||
|
||||
static char *i64toa(char *buf_end, int64_t n, unsigned int base)
|
||||
{
|
||||
char *q = buf_end;
|
||||
|
@ -11339,15 +11341,20 @@ static char *i64toa(char *buf_end, int64_t n, unsigned int base)
|
|||
n = -n;
|
||||
}
|
||||
*--q = '\0';
|
||||
do {
|
||||
digit = (uint64_t)n % base;
|
||||
n = (uint64_t)n / base;
|
||||
if (digit < 10)
|
||||
digit += '0';
|
||||
else
|
||||
digit += 'a' - 10;
|
||||
*--q = digit;
|
||||
} while (n != 0);
|
||||
if (base == 10) {
|
||||
/* division by known base uses multiplication */
|
||||
do {
|
||||
digit = (uint64_t)n % 10;
|
||||
n = (uint64_t)n / 10;
|
||||
*--q = '0' + digit;
|
||||
} while (n != 0);
|
||||
} else {
|
||||
do {
|
||||
digit = (uint64_t)n % base;
|
||||
n = (uint64_t)n / base;
|
||||
*--q = digits[digit];
|
||||
} while (n != 0);
|
||||
}
|
||||
if (is_neg)
|
||||
*--q = '-';
|
||||
return q;
|
||||
|
@ -11595,6 +11602,80 @@ static JSValue js_dtoa(JSContext *ctx,
|
|||
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, JSValueConst val, BOOL is_ToPropertyKey)
|
||||
{
|
||||
uint32_t tag;
|
||||
|
@ -40975,8 +41056,16 @@ static JSValue js_number_toString(JSContext *ctx, JSValueConst this_val,
|
|||
if (base < 0)
|
||||
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))
|
||||
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);
|
||||
fail:
|
||||
JS_FreeValue(ctx, val);
|
||||
|
|
Loading…
Reference in a new issue