FP32 Grids and a Branch That Returned 11.5
A Handwired CPU replay shows how declared grids change comparison circuits, why rounding points matter, and where composition still refuses to build.
I built a circuit for x > 4 ? 10 : 12 and gave it 4.25. With a finite range declaration, it returned 10. After I falsely promised that x was an integer, the smaller circuit returned 11.5. The grid validator rejected the input, but the compiled evaluator itself accepted it.
That is an intentional contract violation in a synthetic fixture. It exposes an important boundary in Handwired: declarations enable cheaper constructions, and the caller must make those declarations true. A compiled arithmetic graph can agree with its reference evaluator while failing to implement the discrete program the caller intended.
I checked revision 4ea7fcb27ebe93acf49b9f29a93e162b975bad46 using Node 24.19.0 on Linux. I executed the unchanged numeric runtime, without a browser, WebGPU, training, or model weights. The results below concern CPU graph construction and evaluation.
A Circuit Builder With Numeric Contracts
Handwired exposes a builder for operations, source declarations, and output wires. Programs explicitly emit their arithmetic and decisions. Source cells live in a Float32Array; instructions append values after them. The inspected runtime supports affine forms, ReGLU, boolean rounding, and indexed reads, with macros that expand comparisons and other familiar expressions into those instructions.
Its compiler collects live instructions into dependency layers and removes operations that cannot reach the requested roots. The repository also contains UI and JSON-search applications, examples, documentation, and tests. The substantive contribution is a working numeric execution model with declared contracts and validation paths, rather than a claim that a trained model learned to execute JavaScript.
I built the first graph as follows:
const c = new Circuit(1)
const x = c.source(0)
c.assumeRange(x, 0, 16)
const gate = emitCompare(c, x, '>', c.constant(4))
const result = emitSelect(c, gate, c.constant(12), c.constant(10))
The selection arguments are the gate, the value when off, and the value when on. For 4.25, the range-only comparison produces an exact decision bit of one. The result is ten.
The execution-model documentation distinguishes range declarations from grid declarations. assumeRange(x, 0, 16) permits fractional FP32 values in that range. assumeGrid(x, 1) additionally promises that every value is a whole number.
The Smaller Graph Depends on That Promise
I rebuilt the circuit with assumeGrid(x, 1) before emitting its readers. Both versions requested the same two output roots: the comparison gate and the selected result. Counting live compiled operations as program.valueCount - program.sourceSize gave:
| Declaration | Input | Gate | Result | Live compiled operations |
|---|---|---|---|---|
| Range 0..16 | 4.25 | 1 | 10 | 22 |
| Range 0..16 plus false integer grid | 4.25 | 0.25 | 11.5 | 8 |
The operation count covers the complete requested graph, including selection. It is not a comparison-only count or a latency benchmark.
The grid-based construction can use the separation between adjacent integer values. A fractional input between four and five then produces a fractional gate. Feeding that gate into selection blends the branches. The circuit is doing arithmetic under a broken premise.
For every integer from zero through sixteen, both graphs matched the native condition and selected value. All seventeen integer inputs also passed validateGrids(). For the fractional input, validation reported:
wire 0 must be a multiple of 1, got 4.25
The evaluator checks source ranges automatically. It rejected input seventeen in this fixture. It did not automatically run the grid validator, so a value can pass the range check and violate the stronger grid promise. The source-table layer in the project provides additional validation machinery; this probe tests Circuit directly.
The builder also refused a comparison whose operand had no finite declared range. These checks are useful, but they enforce different conditions. The range check cannot stand in for a grid check.
Boundary Checks Need a Plain-Language Oracle
The probe constructs isolated comparisons for >, >=, <, <=, and ===, at integer thresholds zero through sixteen. For each threshold, it tests the threshold itself and its immediately adjacent FP32 values, excluding neighbors outside the declared range.
That produces 245 relation checks. Each compiled result must agree both with the runtime's reference evaluator and with the corresponding native JavaScript comparison on the same FP32 input:
{"validIntegerGridCases":17,"rangeAndDeclarationRejections":"passed"}
{"fp32BoundaryComparisonChecks":245,"result":"passed"}
Agreement between the two circuit evaluators checks one boundary: compilation preserved the graph's result. Agreement with the native comparison checks another: the isolated graph implemented the intended relation for those inputs. Neither result proves every FP32 pair or every composition of macros.
The fixtures deliberately inspect neighboring representable values. Testing only whole numbers would have missed the distinction that makes the false-grid example interesting.
Composition Still Refused to Build
My first version put all five relations against zero into one circuit, in the order >, >=, <, <=, ===. Construction failed while adding <=. The probe preserves that reproduction as a separate check. The error begins:
operations that already read wire 15 were built without a grid
The rest of the message says that declaring a grid afterward cannot change existing operations and advises declaring it before readers or allowing existing readers explicitly. This came from the builder's own declaration guard during macro construction. I did not establish a complete root cause or patch the runtime.
The 245 successful checks therefore use independent comparison circuits. They do not establish that this combined graph builds. Recording that distinction prevents a small numeric success from becoming an unsupported claim about the whole builder.
Rounding Changes an Ordinary Minimum
A second fixture compared emitMinimum() with emitMinimumExact(). Both inputs had declared ranges of zero through 16,384. The source array stored the FP32 versions of 1000.37 and 0.3:
{"minimumInput":[1000.3699951171875,0.30000001192092896],"arithmeticMinimum":0.29998779296875,"exactMinimum":0.30000001192092896}
The arithmetic construction uses a - relu(a - b). Cancellation and intermediate FP32 rounding make its result differ from either original input here. The exact construction compares and selects an operand; it returned the same value as Math.min() on this source array.
The expression contracts document this difference. I reproduced one fractional counterexample, not a new universal failure of minimum or a proof of the exact variant across its full range. The practical lesson is specific: algebraically equivalent real-number formulas can carry different FP32 contracts.
Replay Without the Demo Toolchain
The probe verifies source blob 7b225d76cfcb2475e8639892f0466cba5b6cfec3, then imports the original TypeScript module using Node's built-in support. The module has no runtime imports. I ran:
node public/blog/declared-grids-fp32-decisions/numeric-probe.mjs /tmp/handwired-note/numeric-ui-core.ts
The process exited with code zero. No package installation was needed for this runtime-only test. The project's full test command uses Bun; that suite and the GPU demos were not run.
To repeat the probe from a checkout:
git clone https://github.com/chenglou/handwired handwired
git -C handwired checkout 4ea7fcb27ebe93acf49b9f29a93e162b975bad46
node numeric-probe.mjs ./handwired
Its final line is:
PASS: grid contracts, 245 FP32 boundary comparisons, and minimum rounding fixture
The useful engineering boundary is explicit. Declare ranges and grids before building their readers, validate the promised input structure, and compare a compiled result with an independent program oracle. Exactness belongs to those contracts and tested paths; the tensor representation alone cannot supply it.