diff options
author | Charlie Gordon <github@chqrlie.org> | 2024-03-02 15:13:18 +0100 |
---|---|---|
committer | bptato <nincsnevem662@gmail.com> | 2024-03-02 18:12:24 +0100 |
commit | b839e252b6a08bc709fc05a092fb203208d53399 (patch) | |
tree | 6be3d98e1a6317414aace4f8e7bf7bb3c913f543 /lib | |
parent | 712d129700e7e70f93f379f8f8249ea57e573310 (diff) | |
download | chawan-b839e252b6a08bc709fc05a092fb203208d53399.tar.gz |
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
Diffstat (limited to 'lib')
-rw-r--r-- | lib/quickjs/quickjs.c | 107 |
1 files changed, 98 insertions, 9 deletions
diff --git a/lib/quickjs/quickjs.c b/lib/quickjs/quickjs.c index 36720661..dd156e20 100644 --- a/lib/quickjs/quickjs.c +++ b/lib/quickjs/quickjs.c @@ -11430,6 +11430,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; @@ -11441,15 +11443,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; @@ -11697,6 +11704,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; @@ -41083,8 +41164,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); |