diff --git a/quickjs.c b/quickjs.c index f8c07d2..e34270f 100644 --- a/quickjs.c +++ b/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);