mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-21 05:50:55 +08:00
String to number: Number(str), unary +, and parseFloat now convert short decimal spellings with Clinger's exact fast path (scr_decimal_scan in scr_number.c): significant digits m <= 2^53 and a decimal exponent within +-22 (plus the folded range up to 22 + 15) need one correctly rounded multiply or divide, bit-identical to strtod. ToNumber validates and accumulates in the same pass; everything else still goes through the copied span and strtod. Whitespace trimming in Number(), parseInt, and parseFloat skips the UTF-8 decode for ASCII non-whitespace. toFixed: the exact-binary-value digit generation moves from scr_lib.c into scr_number.c (scr_f64_to_fixed). For f <= 22, mantissa * 5^f fits a 64x64->128 multiply, so the spec's n is one shift plus the dropped-half bit whenever it fits 64 bits. Digits render two at a time instead of through snprintf. The 16-limb bignum remains the fallback. Number to string (H44): a non-integral x below 2^46 that is the double nearest to m/100 prints as m/100 directly instead of running Ryu. Below 2^46 ulp(x) < 0.01, so m/100 is the unique shortest round-tripping decimal. The correctly rounded division (double)m / 100 == x is the proof. This covers JSON.stringify, templates, String(), join, and console output. Tests: Node oracles for every path. number-cases.txt gains money-like values, the 2^46 cutoff, and one-ulp neighbors. tonumber-cases.txt gains fast-path boundaries plus 20k seeded short decimals. The new parsefloat-cases.txt pins scr_parse_float, and the new fixed-cases.txt/test_fixed.c pin toFixed, including the 2^64 fast-path limit and f up to 100. Fuzz modes cover all four paths. Existing case lines are unchanged. Corpus 3170 pins the paths end-to-end against Node. Linux x64 sandbox, A/B against perf/stack/06-h23-layout-noise, --layouts=4 --runs=16 (run 3 of 3; geomean -6.0% / -6.7% / -6.3%): | workload | baseline ms | candidate ms | change | 95% CI | verdict | size | node ms | candidate/node | | --- | ---: | ---: | ---: | --- | --- | ---: | ---: | ---: | | json-records | 236.2 | 204.2 | -13.5% | -19.8% .. +2.1% | neutral | +0.0% | 488.62 | 0.42x | | regex-logs | 176.2 | 168.8 | -4.2% | -10.1% .. +0.3% | neutral | +0.0% | 281.23 | 0.60x | | ast-interp | 280.0 | 273.1 | -2.5% | -5.6% .. +1.4% | neutral | +2.8% | 273.05 | 1.00x | | records-sort | 952.4 | 939.4 | -1.4% | -5.0% .. +5.6% | neutral | +0.0% | 289.87 | 3.24x | | template-render | 78.9 | 77.3 | -2.1% | -6.2% .. +1.2% | neutral | +0.0% | 246.71 | 0.31x | | functional-pipeline | 338.7 | 348.8 | +3.0% | -6.5% .. +24.0% | neutral | +0.1% | 339.84 | 1.03x | | async-pipeline | 207.3 | 207.8 | +0.2% | -5.3% .. +3.8% | neutral | +0.0% | 105.57 | 1.97x | | word-graph | 302.0 | 300.1 | -0.6% | -5.1% .. +12.9% | neutral | +3.0% | 600.48 | 0.50x | | alloc-trees | 336.3 | 332.4 | -1.1% | -6.2% .. +2.6% | neutral | +0.1% | 144.73 | 2.30x | | numeric-kernels | 49.7 | 49.8 | +0.1% | -3.4% .. +6.5% | neutral | +4.0% | 143.73 | 0.35x | | validate-errors | 179.1 | 182.9 | +2.1% | -5.8% .. +9.1% | neutral | +0.0% | 727.78 | 0.25x | | csv-numbers | 349.0 | 233.8 | -33.0% | -37.5% .. -26.5% | faster | +0.1% | 541.77 | 0.43x | | log-summary | 171.6 | 135.7 | -20.9% | -23.3% .. -17.7% | faster | +1.8% | 313.2 | 0.43x | | inventory-report | 411.9 | 384.7 | -6.6% | -18.1% .. +5.5% | neutral | +2.7% | 521.27 | 0.74x | Size: +64..+136 bytes per executable. The +1.8..+4.0% rows are one 4 KiB page crossing (two for inventory-report).