skip to content
[readonly]

The cluster float mode Z: f32 arithmetic with subnormal inputs and results flushed to signed zero, as the PTX .ftz ops

sh
$ 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
==> README.md <==
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.)