Float16Array vs Float32Array for Browser AI
Measure storage and binary16 error on deterministic vectors, expose the double-rounding trap, and gate JavaScript and WebGPU capabilities separately.
Float16Array vs Float32Array is a storage and boundary decision for browser AI, not a promise that every GPU operation will run in half precision. This comparison measures bytes and round-trip error on fixed vectors, includes a binary16 double-rounding counterexample, and keeps JavaScript support separate from WebGPU shader support.
Float16Array vs Float32Array separates four questions
A typed array answers how JavaScript stores values in an ArrayBuffer. It does not, by itself, determine how a model computes, how a GPU shader validates, or whether a browser exposes the feature. Float16Array vs Float32Array must therefore be split into storage width, conversion error, transfer behavior, and execution capability.
Float32Array stores one IEEE binary32 value in four bytes. Float16Array stores one binary16 value in two bytes and converts assigned JavaScript Numbers to that narrower representation. A million elements occupy roughly 4 MB or 2 MB of payload respectively, before container, alignment, model, and runtime overhead.
The ECMAScript 2026 indexed-collections specification is the normative source for the language object and its conversion semantics. Availability still depends on the user’s engine and release.
Float16Array vs Float32Array therefore compares typed arrays and half-precision storage first; binary16 rounding and WebGPU f16 remain separately verified boundaries.
Start with an exact storage receipt: constructor support, element count, byte length, source hash, and round-trip vector. Then add error metrics. Only after both pass should the product consider upload timing or shader execution. This ordering prevents a smaller buffer from being marketed as faster inference before any inference path has been measured.

| Format | Bytes | Sign | Exponent | Fraction |
|---|---|---|---|---|
| Binary16 | 2 | 1 bit | 5 bits | 10 bits |
| Binary32 | 4 | 1 bit | 8 bits | 23 bits |
Reading rule: labels, values, patterns, and structure carry the conclusion; color is supplementary.
Read the bit budgets as ranges and precision
Binary16 uses one sign bit, five exponent bits, and ten explicitly stored fraction bits. Binary32 uses one sign bit, eight exponent bits, and twenty-three fraction bits. Normalized values have an implicit leading one, so their effective significand precision is eleven and twenty-four bits respectively.
The smaller exponent field narrows finite range, while the smaller fraction coarsens representable steps. Around one, binary16 spacing is 2⁻¹⁰; binary32 spacing is 2⁻²³. Near zero, subnormal behavior matters. Large finite source values may become infinity, and small ones may become signed zero. NaN and infinities require explicit policy in tensor pipelines.
Figure one labels both layouts and lists payload bytes, exponent width, fraction width, and the storage conclusion. Pattern and text carry meaning so purple and green are not the only comparison.
Use the MXFP4 quantization block audit for a much lower-bit block-scaled format. Float16Array vs Float32Array is not interchangeable with model quantization: there is no learned scale or block metadata in an ordinary typed array, and narrowing every tensor may harm different distributions differently.
Measure error on representative tensor slices
Choose vectors from the actual boundary under consideration: embeddings, activations, weights, or outputs. Freeze a bounded sample with provenance, then record binary16 round-trip values, absolute error, relative error where the reference is nonzero, maximum error, mean error, root-mean-square error, overflow count, underflow-to-zero count, and non-finite count.
Aggregate metrics are not enough. Slice by magnitude and semantic role. A small mean can hide saturation in a rare but important channel. Define release tolerances before seeing the candidate, and include a Float32Array round trip as the control path.
Figure two plots storage bytes against observed error for several deterministic vectors. It is a decision frontier, not a universal benchmark. The article makes no invented latency or model-quality claim because those depend on device, browser, backend, model, and tensor placement.
The AWQ vs GPTQ comparison illustrates how model-aware quantization decisions require evaluation beyond representation math. Apply the same discipline here: the typed-array receipt can approve a storage boundary, but task quality and runtime measurements approve the product change.
Include the double-rounding counterexample
A subtle conversion trap appears when a decimal or binary64 JavaScript Number is rounded to binary32 and then to binary16. That two-step result can differ from direct Number-to-binary16 rounding near a binary16 midpoint. A polyfill that writes through Float32Array before extracting half bits can therefore violate the intended conversion.
The TC39 Float16Array proposal repository documents motivation, implementation work, and test cases, including double-rounding concerns. Treat it as implementation context while using the ECMAScript specification for the final normative semantics.
The lab ships a frozen midpoint-adjacent counterexample and compares a direct exact reference converter with a deliberately wrong via-float32 converter. The negative fixture must produce different half bits and must identify DOUBLE_ROUNDING. If both paths agree, the fixture or implementation has regressed.
Do not substitute Math.fround(value) as a harmless optimization. It intentionally narrows to binary32. Float16Array vs Float32Array requires observing the direct half conversion contract, particularly when exact deterministic serialization matters across native and fallback paths.
- Freeze a representative vector and provenance.
- Record byte length and exact round-trip values.
- Measure maximum, mean, RMS, relative, overflow, and underflow error.
- Apply predeclared tensor-specific tolerances.
- Evaluate task output and runtime separately.
Reading rule: labels, values, patterns, and structure carry the conclusion; color is supplementary.
Gate JavaScript storage and WebGPU separately
The JavaScript gate asks whether globalThis.Float16Array exists and whether its basic constructor, byte length, assignment, and readback pass a canary. The WebGPU gate asks whether an adapter and device support the shader-f16 feature and whether the intended WGSL module validates. One gate can pass while the other fails.
The WGSL f16 extension defines the shader-language feature and constraints. A Float16Array buffer can still be uploaded as packed data without every shader arithmetic path using f16, and an f16-capable device does not retroactively add a missing JavaScript constructor.
Figure three presents four states: neither capability, JavaScript storage only, WebGPU f16 only, or both. Every state has an explicit product action. The conservative fallback is Float32Array storage and a shader path that does not require f16.
The WebNN vs WebGPU browser-AI comparison extends the capability decision to operator coverage, fallback, latency tails, and output parity. Keep this article narrower: choose and validate one tensor storage boundary before selecting or promoting an inference backend.
Model transfer bytes without inventing speedups
For N elements, payload is 2N bytes for half-precision storage and 4N bytes for single precision. That exact difference can reduce network, cache, copy, or upload bytes when the surrounding format and endpoint preserve it. It does not guarantee a twofold wall-clock improvement.
Conversion may cost CPU time. Buffers may need alignment or unpacking. A backend may widen values to binary32 for arithmetic. Memory bandwidth might not be the bottleneck. Shader-f16 support can vary by adapter, driver, and browser. Measure parse, conversion, upload, warm-up, steady-state latency, memory, and output parity on named cohorts.
If model files already use a packed format, avoid decoding into Float32Array only to narrow immediately into Float16Array. Prefer a validated direct binary16 path whose endianness and bit preservation are explicit. Conversely, do not retain compact bytes if every consumer requires repeated widening and the measured product tradeoff is negative.
Float16Array vs Float32Array should appear in a boundary table: source format, in-memory representation, transfer payload, shader declaration, arithmetic type, fallback, and observed metrics. One “FP16 enabled” flag collapses too many independent facts to be useful.
| Float16Array | WebGPU f16 | Action |
|---|---|---|
| No | No | Use Float32 storage and shader. |
| No | Yes | Use validated packed upload or fallback. |
| Yes | No | Half storage may be used; do not claim f16 compute. |
| Yes | Yes | Eligible for measured rollout; no automatic speed claim. |
Reading rule: labels, values, patterns, and structure carry the conclusion; color is supplementary.
Design a reversible browser-AI rollout
Feature-detect at runtime, but make rollout policy server-configurable without embedding user data in query strings. Start with an offline deterministic corpus. Then canary by browser, version, operating system, adapter, and model identity. Preserve a Float32Array fallback and a way to disable narrowing without shipping a new model.
Compare outputs at the task boundary as well as tensor error. Classification labels, generation tokens, embeddings, and image pixels need different parity criteria. Freeze decoding settings where applicable. Report cohorts that fall back instead of merging them into compact-path performance.
Use the WebGPU minimal-surfaces study as a reminder that GPU support and aesthetic output should be verified on the actual rendering stack. For browser AI, the equivalent is a named model, backend, adapter, browser, and capability receipt.
Operational telemetry should contain capability booleans, representation choice, byte counts, bounded error summaries, timings, model hash, and failure codes. Do not collect raw tensors by default. Re-run the fixed vector canary after engine updates because a native constructor becoming available can change which conversion implementation executes.
Export the storage decision receipt
The local lab includes zeros, signed zeros, exact powers, halfway-adjacent values, subnormals, maximum finite binary16, overflow, infinities, NaN, and a representative bounded vector. It uses native Float16Array when available and an independently coded direct reference conversion for the portable oracle.
Every run exports source values in a stable notation, half bits, round-trip values, Float32Array control values, byte lengths, error summaries, special-value counts, constructor capability, declared WebGPU status, and the double-rounding fixture. CSV supports review; JSON preserves the complete receipt and schema version.
The lab does not benchmark a GPU, infer model quality, prove shader arithmetic precision, or recommend narrowing all tensors. It makes no network request and accepts no file or URL input. Its positive and negative fixtures are finite evidence for one storage-conversion contract.
Use Float16Array vs Float32Array as a measured product choice: approve binary16 only when storage savings matter, vector error stays within frozen limits, both capability gates are handled, task outputs pass, and rollback remains tested. Otherwise retain Float32Array. The best representation is not the smallest type in isolation; it is the smallest honest boundary that the complete browser-AI system can support.
Runnable local artifact — The lab measures JavaScript storage conversion on fixed vectors; it is not a GPU benchmark, model-quality evaluation, proof of shader arithmetic width, or universal recommendation to quantize tensors.
Run a bounded vector, including explicitly spelled NaN and signed infinities, through native Float16Array when available and a direct reference converter; report stable string encodings, bytes, absolute and relative error, special-value counts, the TC39 counterexample, and independent JS/WGSL gates.