nx_f64_gate.nx
buildroot/runtime/nx_f64_gate.nx
about
nx_f64_gate.nx -- LN16 "WORKING f64 ARITHMETIC": the x86_64 backend's IEEE-754
binary64 arithmetic AND ordering, measured BIT FOR BIT against independent
software implementations of the same standard.
WHAT THIS MEASURES THAT THE INCUMBENT CANNOT. nx_f64_adversary (T12, shipped
2026-07-16) is a DECIMAL instrument: every check truncates a scaled product to
six or nine digits and compares a hand-typed integer. That shape cannot see a
one-ulp error, cannot say WHICH bit moved, and asks the reader to take six
magic constants on trust. It also compares only NON-NEGATIVE values, which is
why it never found the ordering defect this gate reports. Every expectation
here is DERIVED at run time from nx_f64_oracle_sov (orc_add/orc_sub/orc_mul/
orc_div) and nx_f64 (nx_f64_lt/gt/eq) -- pure-integer implementations whose
anchor proof is the 1217 hardware-IEEE KAT values in _f64_soak_gate_authored.
There is not one hand-typed floating-point constant in this file, and operand
bit patterns come from nx_i64_to_f64, a THIRD path, so an operand and its
expectation are never produced by the same code.
WHY BITS AND NOT PRINTED DECIMALS. Two doubles one ulp apart print identically
at nine significant digits. The defect this rung exists to remove -- f64
arithmetic silently executed at single precision -- is a low-mantissa error, so
a decimal comparison is precisely the instrument that cannot see the end of it.
WHY ORDERING IS IN SCOPE. A double rides in an i64 register, and x86ctx_emit_cmp
lowers EVERY compare to a signed integer cmpq on those bits. Among two negative
doubles the signed-integer order is the REVERSE of the numeric order, so
`-1.0 < -2.0` evaluates TRUE. The rung's own done-rule program compares two f64
values, so ordering is already inside the capability the row claims; flipping
the row while `<` is inverted would publish a capability that is not there.
Tooth `neg-control-chosen-negative-pair-is-exactly-where-integer-compare-fails`
proves the operands chosen are the discriminating ones and not a lucky pair.
HOW A FIXTURE REPORTS. A wait status carries eight bits of exit code. A value
fixture returns 0 when the bits matched and F6_EXITBASE + k when the HIGHEST
differing bit is k (2..65, inside 255), so a failure NAMES the field that moved:
63 sign, 52..62 exponent, 0..51 significand. A predicate fixture returns
F6_EXITBASE + the boolean the compiler actually produced, so a failure says
which way it went. A pass/fail bit would have said only "wrong".
HOW THE BITS ARE READ. NishiLang carries an f64 in an i64 register and `as` is a
BIT-REINTERPRET, never a value conversion -- only the __f64_to_i64 intrinsic
dependencies 6 imports · 0 importers
imports: nx_syscalls.nxnx_gate_verdict.nxnx_ccbuild_lib.nxnx_f64.nxnx_f64_cvt.nxnx_f64_oracle_sov.nx
imported by: nobody (leaf or entry point)
call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown
structs
| none |
consts
| 80 | const F6_EXIT_USAGE: i64 = 2 |
| 81 | const F6_EXIT_NOTREE: i64 = 4 |
| 82 | const F6_MODE_0644: i64 = 0x1a4 |
| 83 | const F6_MODE_0755: i64 = 0x1ed |
| 86 | const F6_BITS: i64 = 64 |
| 90 | const F6_EXITBASE: i64 = 2 |
| 92 | const F6_WAIT_SHIFT: i64 = 8 |
| 93 | const F6_WAIT_MASK: i64 = 255 |
| 95 | const F6_DIGITS_MAX: i64 = 20 |
| 96 | const F6_PATH_CAP: i64 = 4096 // Linux PATH_MAX |
| 99 | const F6_INT_N: i64 = 100 |
| 100 | const F6_INT_MUL: i64 = 3 |
| 101 | const F6_INT_SUB: i64 = 1 |
| 105 | const F6_ACASES: i64 = 12 |
| 106 | const F6_PSLOT: i64 = 20 |
| 107 | const F6_PCASES: i64 = 6 |
| 108 | const F6_SLOT_NAN: i64 = 30 // LN38: the IEEE-unordered fixture program |
| 109 | const F6_SLOT_LIT: i64 = 31 // LN36/LN40: exponent and long-literal packing fixture |
| 110 | const F6_SLOT_CONST: i64 = 32 // LN39: f64 const-expression fixture |
| 111 | const F6_SLOT_IOVER: i64 = 33 // LN40: integer literal that exceeds i64 must be REFUSED |
| 112 | const F6_SLOT_EXP: i64 = 34 // LN36: a malformed exponent must be REFUSED |
| 113 | const F6_SLOT_IMAX: i64 = 35 // neg-control: the largest i64 literal still compiles |
| 114 | const F6_SLOT_U64: i64 = 36 // positive control: a u64-range decimal keeps its bit pattern (FNV constants) |
| 118 | const F6_CCLOG_READ_CAP: i64 = 65536 |
| 119 | const F6_SLOT_PROBE: i64 = 80 |
| 120 | const F6_SLOT_INT_A: i64 = 90 |
| 121 | const F6_SLOT_INT_B: i64 = 91 |
| 122 | const F6_SLOTS: i64 = 92 |
functions
| 124 | func f6_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } |
| 130 | func f6_t0() -> *u8 { return "func main(argc: i64, argv: *i64) -> i64 {\n\x00" as *u8 } called by 1: f6_build_src |
| 131 | func f6_t1() -> *u8 { return " let r: f64 = \x00" as *u8 } called by 1: f6_build_src |
| 132 | func f6_t2() -> *u8 { return "\n let got: i64 = r as i64\n let want: i64 = \x00" as *u8 } called by 1: f6_build_src |
| 133 | func f6_t3() -> *u8 { return "\n if got == want { return 0 }\n var k: i64 = \x00" as *u8 } called by 1: f6_build_src |
| 134 | func f6_t4() -> *u8 { return "\n while k >= 0 {\n let gb: i64 = (got >> k) & 1\n let wb: i64 = (want >> k) & 1\n if gb > wb { return \x00" as *u8 } called by 1: f6_build_src |
| 135 | func f6_t5() -> *u8 { return " + k }\n if wb > gb { return \x00" as *u8 } called by 1: f6_build_src |
| 136 | func f6_t6() -> *u8 { return " + k }\n k = k - 1\n }\n return 1\n}\n\x00" as *u8 } called by 1: f6_build_src |
| 139 | func f6_q0() -> *u8 { return "func main(argc: i64, argv: *i64) -> i64 {\n let a: f64 = \x00" as *u8 } called by 1: f6_pred_src |
| 140 | func f6_q1() -> *u8 { return "\n let b: f64 = \x00" as *u8 } called by 1: f6_pred_src |
| 141 | func f6_q2() -> *u8 { return "\n var got: i64 = 0\n if a \x00" as *u8 } called by 1: f6_pred_src |
| 142 | func f6_q3() -> *u8 { return " b { got = 1 }\n let want: i64 = \x00" as *u8 } called by 1: f6_pred_src |
| 143 | func f6_q4() -> *u8 { return "\n if got == want { return 0 }\n return \x00" as *u8 } called by 1: f6_pred_src |
| 144 | func f6_q5() -> *u8 { return " + got\n}\n\x00" as *u8 } called by 1: f6_pred_src |
| 146 | func f6_d0() -> *u8 { return " let a: f64 = \x00" as *u8 } called by 1: f6_decls2 |
| 147 | func f6_d1() -> *u8 { return "\n let b: f64 = \x00" as *u8 } called by 1: f6_decls2 |
| 148 | func f6_d2() -> *u8 { return "\n\x00" as *u8 } called by 1: f6_decls2 |
| 149 | func f6_c0() -> *u8 { return "__f64_from_i64(\x00" as *u8 } called by 1: f6_fromi |
| 150 | func f6_c1() -> *u8 { return ")\x00" as *u8 } called by 1: f6_fromi |
| 151 | func f6_n0() -> *u8 { return "0 - \x00" as *u8 } called by 1: f6_catwant |
| 156 | func f6_catwant(b: *u8, o: i64, v: i64) -> i64 |
| 162 | func f6_build_src(decls: *u8, expr: *u8, want: i64, outlen: *i64) -> *u8 |
| 185 | func f6_pred_src(aexpr: *u8, bexpr: *u8, op: *u8, want: i64, outlen: *i64) -> *u8 |
| 206 | func f6_decls2(a: *u8, b: *u8) -> *u8 |
| 222 | func f6_fromi(n: i64) -> *u8 |
| 232 | func f6_path(pre: *u8, idx: i64, suf: *u8) -> *u8 |
| 240 | func f6_tmp(idx: i64, suf: *u8) -> *u8 { return f6_path("/tmp/nx_f64_gate/c\x00" as *u8, idx, suf) } |
| 241 | func f6_bld(idx: i64, suf: *u8) -> *u8 { return f6_path("_build/nx_f64_gate_c\x00" as *u8, idx, suf) } |
| 243 | func f6_write(path: *u8, s: *u8, n: i64) -> i64 |
| 253 | func f6_build_run(cc: *u8, idx: i64, p_nx: *u8, envp: *i64, devnull: i64, out: *i64) -> i64 |
| 273 | func f6_run_src(cc: *u8, idx: i64, src: *u8, srclen: i64, envp: *i64, devnull: i64, out: *i64) -> i64 |
| 279 | func f6_report(idx: i64, label: *u8, want: i64, kindbits: i64, out: *i64) -> i64 |
| 299 | func f6_case(cc: *u8, idx: i64, label: *u8, decls: *u8, expr: *u8, want: i64, |
| 310 | func f6_pcase(cc: *u8, idx: i64, label: *u8, aexpr: *u8, bexpr: *u8, op: *u8, want: i64, |
| 323 | func f6_nan_src() -> *u8 called by 1: main |
| 325 | func fx_id(x: f64) -> f64 { return x } |
| 326 | func main() -> i64 |
| 353 | func f6_lit_src() -> *u8 { return "func fx_id(x: f64) -> f64 { return x }\nfunc main() -> i64 {\n var bad: i64 = 0\n let ten9: f64 = fx_id(1000000000.0)\n let e21: f64 = fx_id(1000000000000000000000.0)\n let e20: f64 = fx_id(100000000000000000000.0)\n if 1e9 != ten9 { bad = bad + 1 }\n if 2.5e-3 != fx_id(2.5) / fx_id(1000.0) { bad = bad + 2 }\n if 1e-9 != fx_id(1.0) / ten9 { bad = bad + 4 }\n if 6.02e23 != fx_id(602.0) * e21 { bad = bad + 8 }\n if 1e-20 != fx_id(1.0) / e20 { bad = bad + 16 }\n if 1E+2 != fx_id(100.0) { bad = bad + 32 }\n if 123.456e2 != fx_id(12345.6) { bad = bad + 64 }\n if 9007199254740993.0 != fx_id(9007199254740992.0) { bad = bad + 128 }\n if 9007199254740995.0 != fx_id(9007199254740996.0) { bad = bad + 256 }\n if 0.1 + 0.2 == 0.3 { bad = bad + 512 }\n if 1e400 <= 1e308 { bad = bad + 1024 }\n if 12345678901234567890.5 != fx_id(12345678901234567890.0) + fx_id(0.5) { bad = bad + 2048 }\n if 1e0 != fx_id(1.0) { bad = bad + 4096 }\n return bad\n}\n\x00" as *u8 } |
| 354 | func f6_const_src() -> *u8 { return "const PI: f64 = 3.141592653589793\nconst HALF_PI: f64 = PI / 2.0\nconst TWO_PI: f64 = 2.0 * PI\nconst NEG_1P5: f64 = -1.5\nconst K: i64 = 3\nconst KF: f64 = K * 2.0\nconst E9: f64 = 1e9\nconst MIX: f64 = (PI - 1.0) / (K + 1)\nfunc fx_id(x: f64) -> f64 { return x }\nfunc main() -> i64 {\n var bad: i64 = 0\n let pi: f64 = fx_id(3.141592653589793)\n if HALF_PI != pi / fx_id(2.0) { bad = bad + 1 }\n if TWO_PI != fx_id(2.0) * pi { bad = bad + 2 }\n if NEG_1P5 != fx_id(0.0) - fx_id(1.5) { bad = bad + 4 }\n if KF != fx_id(6.0) { bad = bad + 8 }\n if E9 != fx_id(1000000000.0) { bad = bad + 16 }\n if MIX != (pi - fx_id(1.0)) / fx_id(4.0) { bad = bad + 32 }\n if PI != pi { bad = bad + 64 }\n return bad\n}\n\x00" as *u8 } |
| 355 | func f6_iover_src() -> *u8 { return "func main() -> i64 {\n let x: i64 = 99999999999999999999\n if x == 0 { return 1 }\n return 0\n}\n\x00" as *u8 } |
| 356 | func f6_badexp_src() -> *u8 { return "func main() -> i64 {\n let y: f64 = 1e-\n if y == 0.0 { return 1 }\n return 0\n}\n\x00" as *u8 } |
| 357 | func f6_imax_src() -> *u8 { return "func main() -> i64 {\n let x: i64 = 9223372036854775807\n if x == 9223372036854775807 { return 0 }\n return 1\n}\n\x00" as *u8 } |
| 359 | func f6_u64_src() -> *u8 { return "func main() -> i64 {\n let x: i64 = 18446744073709551615\n let y: i64 = 14695981039346656037\n if x != 0 - 1 { return 1 }\n if y == 0 { return 2 }\n return 0\n}\n\x00" as *u8 } |
| 363 | func f6_file_has(path: *u8, needle: *u8) -> i64 |
| 386 | func f6_int_sum() -> i64 |
| 392 | func f6_i0() -> *u8 { return "func main(argc: i64, argv: *i64) -> i64 {\n var s: i64 = 0\n var i: i64 = 0\n while i < \x00" as *u8 } |
| 393 | func f6_i1() -> *u8 { return " {\n s = s + i * \x00" as *u8 } |
| 394 | func f6_i2() -> *u8 { return " - \x00" as *u8 } |
| 395 | func f6_i3() -> *u8 { return "\n i = i + 1\n }\n if s == \x00" as *u8 } |
| 396 | func f6_i4() -> *u8 { return " { return 0 }\n return 1\n}\n\x00" as *u8 } |
| 398 | func f6_int_src(outlen: *i64) -> *u8 |
| 416 | func main(argc: i64, argv: *i64) -> i64 |