@scriptc/runtime 0.0.16 → 0.0.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/scr_lib.c +164 -0
- package/src/scr_runtime.h +6 -4
package/package.json
CHANGED
package/src/scr_lib.c
CHANGED
|
@@ -3503,6 +3503,170 @@ ScrStr *scr_num_to_fixed0(double x) {
|
|
|
3503
3503
|
return r;
|
|
3504
3504
|
}
|
|
3505
3505
|
|
|
3506
|
+
/* The explicit-fraction-digits toFixed needs the exact binary value, not
|
|
3507
|
+
* the shortest decimal that round-trips to it: (1.005).toFixed(2) is
|
|
3508
|
+
* "1.00", for example. Represent abs(x) * 10^f as
|
|
3509
|
+
*
|
|
3510
|
+
* mantissa * 5^f * 2^(binary_exponent + f)
|
|
3511
|
+
*
|
|
3512
|
+
* in a tiny base-2^32 integer, then shift right with the spec's
|
|
3513
|
+
* round-half-up rule. The largest value handled here is below 1e21 with
|
|
3514
|
+
* f=100, so the rounded integer is below 1e121 (402 bits); sixteen limbs
|
|
3515
|
+
* leave comfortable headroom without heap allocation. */
|
|
3516
|
+
#define SCR_FIXED_LIMBS 16
|
|
3517
|
+
typedef struct {
|
|
3518
|
+
uint32_t limb[SCR_FIXED_LIMBS]; /* little-endian */
|
|
3519
|
+
int len;
|
|
3520
|
+
} ScrFixedInt;
|
|
3521
|
+
|
|
3522
|
+
static void scr_fixed_normalize(ScrFixedInt *v) {
|
|
3523
|
+
while (v->len > 1 && v->limb[v->len - 1] == 0) v->len--;
|
|
3524
|
+
}
|
|
3525
|
+
|
|
3526
|
+
static void scr_fixed_mul5(ScrFixedInt *v) {
|
|
3527
|
+
uint64_t carry = 0;
|
|
3528
|
+
for (int i = 0; i < v->len; i++) {
|
|
3529
|
+
uint64_t p = (uint64_t)v->limb[i] * 5 + carry;
|
|
3530
|
+
v->limb[i] = (uint32_t)p;
|
|
3531
|
+
carry = p >> 32;
|
|
3532
|
+
}
|
|
3533
|
+
if (carry != 0) v->limb[v->len++] = (uint32_t)carry;
|
|
3534
|
+
}
|
|
3535
|
+
|
|
3536
|
+
static bool scr_fixed_bit(const ScrFixedInt *v, int bit) {
|
|
3537
|
+
int word = bit / 32;
|
|
3538
|
+
return word < v->len && ((v->limb[word] >> (bit % 32)) & 1u) != 0;
|
|
3539
|
+
}
|
|
3540
|
+
|
|
3541
|
+
static void scr_fixed_shr(ScrFixedInt *v, int bits) {
|
|
3542
|
+
int words = bits / 32;
|
|
3543
|
+
int rem = bits % 32;
|
|
3544
|
+
if (words >= v->len) {
|
|
3545
|
+
v->limb[0] = 0;
|
|
3546
|
+
v->len = 1;
|
|
3547
|
+
return;
|
|
3548
|
+
}
|
|
3549
|
+
int n = v->len - words;
|
|
3550
|
+
for (int i = 0; i < n; i++) {
|
|
3551
|
+
uint32_t lo = v->limb[i + words] >> rem;
|
|
3552
|
+
uint32_t hi =
|
|
3553
|
+
rem != 0 && i + words + 1 < v->len
|
|
3554
|
+
? v->limb[i + words + 1] << (32 - rem)
|
|
3555
|
+
: 0;
|
|
3556
|
+
v->limb[i] = lo | hi;
|
|
3557
|
+
}
|
|
3558
|
+
v->len = n;
|
|
3559
|
+
scr_fixed_normalize(v);
|
|
3560
|
+
}
|
|
3561
|
+
|
|
3562
|
+
static void scr_fixed_shl(ScrFixedInt *v, int bits) {
|
|
3563
|
+
uint32_t out[SCR_FIXED_LIMBS] = {0};
|
|
3564
|
+
int words = bits / 32;
|
|
3565
|
+
int rem = bits % 32;
|
|
3566
|
+
for (int i = 0; i < v->len; i++) {
|
|
3567
|
+
int at = i + words;
|
|
3568
|
+
out[at] |= v->limb[i] << rem;
|
|
3569
|
+
if (rem != 0) out[at + 1] |= v->limb[i] >> (32 - rem);
|
|
3570
|
+
}
|
|
3571
|
+
int n = v->len + words + (rem != 0 ? 1 : 0);
|
|
3572
|
+
memcpy(v->limb, out, sizeof out);
|
|
3573
|
+
v->len = n;
|
|
3574
|
+
scr_fixed_normalize(v);
|
|
3575
|
+
}
|
|
3576
|
+
|
|
3577
|
+
static void scr_fixed_inc(ScrFixedInt *v) {
|
|
3578
|
+
uint64_t carry = 1;
|
|
3579
|
+
for (int i = 0; i < v->len && carry != 0; i++) {
|
|
3580
|
+
uint64_t s = (uint64_t)v->limb[i] + carry;
|
|
3581
|
+
v->limb[i] = (uint32_t)s;
|
|
3582
|
+
carry = s >> 32;
|
|
3583
|
+
}
|
|
3584
|
+
if (carry != 0) v->limb[v->len++] = (uint32_t)carry;
|
|
3585
|
+
}
|
|
3586
|
+
|
|
3587
|
+
/* Divide in place by 1e9; each quotient limb still fits uint32_t because
|
|
3588
|
+
* the carried remainder is below the divisor. Returns the remainder. */
|
|
3589
|
+
static uint32_t scr_fixed_div1e9(ScrFixedInt *v) {
|
|
3590
|
+
uint64_t rem = 0;
|
|
3591
|
+
for (int i = v->len - 1; i >= 0; i--) {
|
|
3592
|
+
uint64_t cur = (rem << 32) | v->limb[i];
|
|
3593
|
+
v->limb[i] = (uint32_t)(cur / 1000000000u);
|
|
3594
|
+
rem = cur % 1000000000u;
|
|
3595
|
+
}
|
|
3596
|
+
scr_fixed_normalize(v);
|
|
3597
|
+
return (uint32_t)rem;
|
|
3598
|
+
}
|
|
3599
|
+
|
|
3600
|
+
/* Number.prototype.toFixed(fractionDigits), with the argument already
|
|
3601
|
+
* number-typed by the frontend. ToIntegerOrInfinity validation precedes
|
|
3602
|
+
* the receiver's non-finite arm, as ECMA-262 requires. Invalid precision
|
|
3603
|
+
* raises V8's catchable RangeError text; otherwise the result is +1. */
|
|
3604
|
+
ScrStr *scr_num_to_fixed(double x, double fraction_digits) {
|
|
3605
|
+
double fd = isnan(fraction_digits) ? 0 : trunc(fraction_digits);
|
|
3606
|
+
if (!(fd >= 0 && fd <= 100)) {
|
|
3607
|
+
static const char msg[] =
|
|
3608
|
+
"toFixed() digits argument must be between 0 and 100";
|
|
3609
|
+
scr_throw_error_msg(SCR_ERR_RANGE, msg, sizeof msg - 1);
|
|
3610
|
+
return NULL;
|
|
3611
|
+
}
|
|
3612
|
+
int f = (int)fd;
|
|
3613
|
+
if (!isfinite(x) || fabs(x) >= 1e21) return scr_f64_to_scrstr(x);
|
|
3614
|
+
|
|
3615
|
+
bool neg = x < 0; /* false for -0, exactly like the spec's sign arm */
|
|
3616
|
+
double a = neg ? -x : x;
|
|
3617
|
+
uint64_t bits;
|
|
3618
|
+
memcpy(&bits, &a, sizeof bits);
|
|
3619
|
+
uint64_t mantissa = bits & ((1ull << 52) - 1);
|
|
3620
|
+
int ieee_exp = (int)((bits >> 52) & 0x7ffu);
|
|
3621
|
+
int binary_exp;
|
|
3622
|
+
if (ieee_exp == 0) {
|
|
3623
|
+
binary_exp = -1074;
|
|
3624
|
+
} else {
|
|
3625
|
+
mantissa |= 1ull << 52;
|
|
3626
|
+
binary_exp = ieee_exp - 1023 - 52;
|
|
3627
|
+
}
|
|
3628
|
+
|
|
3629
|
+
ScrFixedInt n = {{(uint32_t)mantissa, (uint32_t)(mantissa >> 32)}, 2};
|
|
3630
|
+
scr_fixed_normalize(&n);
|
|
3631
|
+
for (int i = 0; i < f; i++) scr_fixed_mul5(&n);
|
|
3632
|
+
int shift = binary_exp + f;
|
|
3633
|
+
if (shift >= 0) {
|
|
3634
|
+
scr_fixed_shl(&n, shift);
|
|
3635
|
+
} else {
|
|
3636
|
+
int right = -shift;
|
|
3637
|
+
bool round_up = scr_fixed_bit(&n, right - 1);
|
|
3638
|
+
scr_fixed_shr(&n, right);
|
|
3639
|
+
if (round_up) scr_fixed_inc(&n);
|
|
3640
|
+
}
|
|
3641
|
+
|
|
3642
|
+
/* Render the exact rounded integer through base-1e9 chunks, then place
|
|
3643
|
+
* the decimal point f digits from the right (padding through zero). */
|
|
3644
|
+
uint32_t chunks[SCR_FIXED_LIMBS];
|
|
3645
|
+
int chunk_count = 0;
|
|
3646
|
+
do {
|
|
3647
|
+
chunks[chunk_count++] = scr_fixed_div1e9(&n);
|
|
3648
|
+
} while (!(n.len == 1 && n.limb[0] == 0));
|
|
3649
|
+
|
|
3650
|
+
char digits[128];
|
|
3651
|
+
int dlen = snprintf(digits, sizeof digits, "%u", chunks[chunk_count - 1]);
|
|
3652
|
+
for (int i = chunk_count - 2; i >= 0; i--) {
|
|
3653
|
+
dlen += snprintf(digits + dlen, sizeof digits - (size_t)dlen,
|
|
3654
|
+
"%09u", chunks[i]);
|
|
3655
|
+
}
|
|
3656
|
+
|
|
3657
|
+
char out[128];
|
|
3658
|
+
int o = 0;
|
|
3659
|
+
if (neg) out[o++] = '-';
|
|
3660
|
+
int padded = dlen > f + 1 ? dlen : f + 1;
|
|
3661
|
+
int integer_digits = padded - f;
|
|
3662
|
+
int leading_zeros = padded - dlen;
|
|
3663
|
+
for (int i = 0; i < padded; i++) {
|
|
3664
|
+
if (f != 0 && i == integer_digits) out[o++] = '.';
|
|
3665
|
+
out[o++] = i < leading_zeros ? '0' : digits[i - leading_zeros];
|
|
3666
|
+
}
|
|
3667
|
+
return scr_str_new(out, (size_t)o);
|
|
3668
|
+
}
|
|
3669
|
+
|
|
3506
3670
|
/* Increment a decimal digit string in place. Returns true on overflow —
|
|
3507
3671
|
* the value becomes 1 followed by len zeros (the caller folds the zeros
|
|
3508
3672
|
* into its scale); an EMPTY string increments to "1" the same way (the
|
package/src/scr_runtime.h
CHANGED
|
@@ -4243,12 +4243,14 @@ bool scr_num_is_finite(double x);
|
|
|
4243
4243
|
bool scr_num_is_nan(double x);
|
|
4244
4244
|
bool scr_num_is_integer(double x);
|
|
4245
4245
|
bool scr_num_is_safe_integer(double x);
|
|
4246
|
-
/*
|
|
4247
|
-
*
|
|
4248
|
-
*
|
|
4249
|
-
*
|
|
4246
|
+
/* Number.prototype formatters: toExponential() is the fraction-digits-free
|
|
4247
|
+
* shortest correctly-rounded mantissa; toFixed0 is the non-throwing omitted
|
|
4248
|
+
* argument form. toFixed implements an explicit 0..100 fractionDigits with
|
|
4249
|
+
* exact binary-value rounding and THROWS V8's catchable RangeError outside
|
|
4250
|
+
* that range. All successful results are +1. */
|
|
4250
4251
|
ScrStr *scr_num_to_exponential(double x);
|
|
4251
4252
|
ScrStr *scr_num_to_fixed0(double x);
|
|
4253
|
+
ScrStr *scr_num_to_fixed(double x, double fraction_digits);
|
|
4252
4254
|
|
|
4253
4255
|
/* Object.is over two numbers — the spec's SameValue on doubles: NaN
|
|
4254
4256
|
* equals NaN, +0 differs from -0, everything else is ==. Never throws. */
|