The cluster float mode Z: f32 arithmetic with subnormal inputs and results flushed to signed zero, as the PTX .ftz ops
$ sexc add @ajax/mesh-ftz- total 3
- drwxr-xr-x-../
- drwxr-xr-x-Oct 27 01:59v/# 1 version
- drwxr-xr-x-Oct 27 01:59files/# the files of 0.1.0
- latest
- 0.1.0, published Oct 27 01:59 by ajax
- owners
- ajax
- license
- MIT
- dependencies
- none
- dependents
- @ajax/mesh-assemble, @ajax/mesh-ref-block, @ajax/mesh-ref-cubic, @ajax/mesh-ref-doom, @ajax/mesh-ref-gpt2, @ajax/mesh-ref-gpt2-attend, @ajax/mesh-ref-gpt2-kv, @ajax/mesh-ref-gpt2-linear, @ajax/mesh-ref-gpt2-logits, @ajax/mesh-ref-gpt2-norm, @ajax/mesh-ref-gpt2-scalar, @ajax/mesh-ref-gpt2-weights, @ajax/mesh-ref-matmul, @ajax/mesh-rt-wasm, @ajax/pathtrace-z
- keywords
- alphaos mesh float ftz determinism
- homepage
- https://github.com/thomasdavis/alpha-c-mesh/tree/main/packages/mesh-ftz
- repository
- https://github.com/thomasdavis/alpha-c-mesh
1# @ajax/mesh-ftz 2 3The cluster float mode `Z`: f32 arithmetic exactly as the mesh's PTX does it (`add/sub/mul/div .rn.ftz`, 4`abs/neg .ftz`, `setp .ftz`, `cvt .ftz`). A subnormal input is flushed to the zero of its sign, the IEEE 5result is correctly rounded, and a subnormal result is flushed the same way. Every CPU reference of the 6mesh is written in these operations, so a CPU computes the GPU's bits. 7 8```c 9#include "mesh/ftz.h" 10float y = mesh_ftz_add(mesh_ftz_mul(a, x), b); /* never a * x + b */ 11if (mesh_ftz_lt(y, 0.0f)) ... 12``` 13 14- A `Z` value is a plain `float`: loads, stores and moves keep the bits; only `mesh_ftz_*` operations flush. 15- `mesh_ftz_flushes()` counts the subnormals flushed since the process started (inputs and results). 16- On wasm32 the float operations are exported, never-inlined functions (`mesh_ftz_add`, …) and nothing 17 else uses a float instruction, so meshd's load-time float guard can prove an FTZ image never bypasses 18 the flush. Elsewhere they are `static inline`. 19- Freestanding (no libc). Compile code that uses it with `-ffp-contract=off` on a target whose 20 `FLT_EVAL_METHOD` is 0 (x86-64, arm64, wasm32, nvptx64; a static assert checks). 21 22**Ported from** `crates/mesh-ref/src/ftz.rs` (alpha-rust-mesh `00b526b`): `flush`, the `ops` module, `Z`. 23 24**Tested** by the two Rust unit tests, ported, and by golden digests from the Rust: all 44×44 pairs of 25special values (zeros, subnormals, the normal boundary, max, infinities, quiet and signalling NaNs with 26payloads), four sweeps of a million random pairs (any bits; tiny exponents; products and quotients 27crossing the subnormal boundary) through every operation, and a million f64 → f32 converts. Each digest 28covers every result's bits, and the flush counts must match too. Passes with gcc and clang, -O0 and -O2. 29 30## Differences 31 32- **NaN payloads when both operands are NaN.** Rust leaves the operand order of a commutative `+` or `*` 33 to the compiler, and x86 returns the NaN of the instruction's first operand, so the Rust result's 34 payload depends on how the code compiled. Here it is always `a`'s NaN (else `b`'s), quieted — what the 35 Rust build produced in every case measured. (A GPU returns the canonical NaN either way.)