zmij-as
v0.1.0
Published
ECMAScript-compatible f64/f32 to string (dtoa/ftoa) for AssemblyScript, built on the Żmij (Schubfach + xjb) core
Maintainers
Readme
Installation
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"]
}
}Usage
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.
API
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.
Performance
Charts are published per release to the docs branch via npm run charts:publish
(scripts/publish-charts.sh) and pinned here by version.
Verification
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 fuzzBenchmarks
npm run bench -- --v8 --wavm
npm run charts:build -- --v8 --wavm
npm run charts:serveArchitecture
The 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.
Credits
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).
License
Contact
Please send all issues to GitHub Issues and to converse, please send me an email at [email protected]
- Email: Send me inquiries, questions, or requests at [email protected]
- 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
