Summary
Number.prototype.toString() (and the abstract ToString(number) operation invoked via String(n), template literals, property access via numeric keys, etc.) emits scientific notation for integers in the range `[1e15, 1e21)`, where the ECMAScript spec mandates fixed-point decimal output.
Per ES2026 §6.1.6.1.13 Number::toString, when `n - k <= 21` (and `k <= n`) the algorithm appends `(n - k)` zeros to the significant digits. Scientific notation is only used when the value's exponent forces it — concretely, integers `<` `1e21` always have a fixed-point representation.
Repro
const cases = [
1e14, 1e15, 1e16, 1e20, 1e21,
9007199254740991, // Number.MAX_SAFE_INTEGER
999999999999999, // 1e15 - 1
4294967295, // 2^32 - 1
4294967296, // 2^32
1234567890123456,
-1e15,
];
for (const n of cases) console.log(n);
| Input |
GocciaScript output |
ECMAScript-correct output |
| `1e14` |
`100000000000000` |
`100000000000000` |
| `1e15` |
`1E15` |
`1000000000000000` |
| `1e16` |
`1E16` |
`10000000000000000` |
| `1e20` |
`1E20` |
`100000000000000000000` |
| `1e21` |
`1E21` |
`1e+21` (lowercase `e+`) |
| `Number.MAX_SAFE_INTEGER` (`9007199254740991`) |
`9.00719925474099E15` |
`9007199254740991` |
| `999999999999999` |
`999999999999999` |
`999999999999999` |
| `1234567890123456` |
`1.23456789012346E15` |
`1234567890123456` |
| `-1e15` |
`-1E15` |
`-1000000000000000` |
Two distinct deviations are visible:
- The threshold for switching to scientific notation kicks in at `1e15` instead of `1e21`. Any integer with magnitude `>=` `1e15` and `<` `1e21` should be rendered as fixed-point.
- When scientific notation is used (correctly, for `>= 1e21`), the format is `1E21` (uppercase `E`, no `+`) but the spec mandates lowercase `e+` — `1e+21`.
Impact
- Surfaced while implementing sparse iteration for `Array.prototype` methods on huge-length array-likes. test262 fixtures like `built-ins/Array/prototype/indexOf/length-near-integer-limit.js` set `arrayLike[Number.MAX_SAFE_INTEGER - N] = value`. Because `ToString(MAX_SAFE_INTEGER - N)` produces a scientific-notation string, the property key is non-canonical and array-index lookups (including the new sparse path's `TryParseArrayIndex`) correctly skip it.
- Any user-visible `String(n)`, `${n}`, `JSON.stringify`, etc. for large integers prints incorrect values that don't roundtrip with `parseFloat`/`Number`.
Where to look
The fix lives in whatever implements ToString for `TGocciaNumberLiteralValue`. The spec algorithm to reproduce is ES2026 §6.1.6.1.13 — find the unique `n`, `k`, `s` triple and dispatch based on `k` vs `n` rather than always emitting scientific form past some heuristic.
Summary
Number.prototype.toString()(and the abstractToString(number)operation invoked viaString(n), template literals, property access via numeric keys, etc.) emits scientific notation for integers in the range `[1e15, 1e21)`, where the ECMAScript spec mandates fixed-point decimal output.Per ES2026 §6.1.6.1.13 Number::toString, when `n - k <= 21` (and `k <= n`) the algorithm appends `(n - k)` zeros to the significant digits. Scientific notation is only used when the value's exponent forces it — concretely, integers `<` `1e21` always have a fixed-point representation.
Repro
Two distinct deviations are visible:
Impact
Where to look
The fix lives in whatever implements ToString for `TGocciaNumberLiteralValue`. The spec algorithm to reproduce is ES2026 §6.1.6.1.13 — find the unique `n`, `k`, `s` triple and dispatch based on `k` vs `n` rather than always emitting scientific form past some heuristic.