The fastest ECMAScript-Number.toString-compatible
f64/f32 → string for
AssemblyScript, built on the
Żmij (Schubfach + xjb) core.
Table of Contents
npm install zmij-aszmij-as is plain exported functions - no compiler transform required. The digit
kernel uses SIMD when available
and falls back to scalar SWAR otherwise.
To enable SIMD, add to your asc command:
--enable simdOr in your asconfig.json:
{
"options": {
"enable": ["simd"]
}
}import { dtoa, dtoa_buffered, ftoa, ftoa_buffered } from "zmij-as";
dtoa(3.14159); // "3.14159"
dtoa(1e21); // "1e+21"
dtoa(-0.0); // "0"
ftoa(0.1); // "0.1"Writing a serializer with its own buffer? Skip the String allocation and write
UTF-16 straight into your buffer:
// UTF-16 written directly into your buffer (>= 64 bytes); no allocation.
const codeUnits = dtoa_buffered(buffer, value);dtoa(x) matches V8 x.toString() / JSON.stringify(x) byte-for-byte -
including Infinity, -Infinity, NaN, -0 → "0", 1e21 → "1e+21",
1e-7 → "1e-7", the fixed-vs-exponential thresholds, and the minimal-width
signed exponent per ECMAScript Specification.
dtoa(value: f64): string // shortest round-trip, ECMAScript-formatted
dtoa_buffered(buffer, value: f64): u32 // write UTF-16 into buffer, return code-unit count
ftoa(value: f32): string // shortest round-trip, ECMAScript-formatted
ftoa_buffered(buffer, value: f32): u32 // write UTF-16 into buffer, return code-unit countBuffer contract. dtoa_buffered / ftoa_buffered write the shortest decimal
as UTF-16 directly into buffer (no intermediate ASCII pass or widening);
buffer needs ≥ 64 bytes. The returned code-unit count is exact - the extra
headroom covers the in-register digit-block stores, which can overshoot the
logical end by up to one 8-char block.
Charts are published per release to the docs branch via npm run charts:publish
(scripts/publish-charts.sh) and pinned here by version.
dtoa/ftoa are checked against V8 ground truth (Number::toString for f64; the
exact BigInt shortest-round-trip for f32) - 0 failures across every power of two
(±1/±0xF ulp), every power of ten, tens of millions of random values, and a
generated assertion suite with 100% line/branch coverage.
# generate the as-test spec from the V8 oracle, then run the suite
npm run gen-spec
npm test
# build the verify wasm + check against V8 (fixed edge + 2M random; pass a count)
npm run verify
node scripts/dtoa/verify.mjs 3000
# open-ended differential fuzz vs V8 (seeded, repro-friendly; --runs/--time/--seed)
npm run fuzznpm run bench -- --v8 --wavm
npm run charts:build -- --v8 --wavm
npm run charts:serveThe shortest-decimal core is a port of Żmij
(Schubfach + xjb single-power-of-ten multiply); the formatter lays out digits per
the ECMA-262 Number::toString decision tree and stores UTF-16 directly.
The shortest-decimal digits are identical to Ryū/Dragonbox (shortest, round-to-nearest-even) - exactly what ECMA-262 mandates; only the surface formatting differs. The core is a port of Żmij by Victor Zverovich (MIT).
Please send all issues to GitHub Issues and to converse, please send me an email at me@jairus.dev
- Email: Send me inquiries, questions, or requests at me@jairus.dev
- GitHub: Visit the official GitHub repository Here
- Website: Visit my official website at jairus.dev
- Discord: Contact me at My Discord or on the AssemblyScript Discord Server


