code wiki / _hdl_build / nx_f32_vec_gate.nx

nx_f32_vec_gate.nx source

↩ module page · 128 lines · 7614 B

1// nx_f32_vec_gate.nx -- KAT for the hardware-f32 vec3 PART (R3), built + run through the 2// SOVEREIGN compiler (nx_cc_sovereign now carries f32). Multi-value KAT: a faked/constant 3// f32 implementation cannot produce 32, 25, 5, [0,0,1], 3 AND a signed -4 all at once, so 4// distinct-correct results = real hardware float composing into vec3. exit 0 = GREEN. AUTHOR=ORGAN. 5import "nx_syscalls.nx" 6import "nx_f32_hw.nx" 7import "nx_f32.nx" 8 9func main() -> i64 { 10 let d: i64 = v3_dot(f32_of(1), f32_of(2), f32_of(3), f32_of(4), f32_of(5), f32_of(6)) 11 if f32_int(d) != 32 { return 1 } // 4+10+18 12 if f32_int(v3_lensq(f32_of(3), f32_of(4), f32_of(0))) != 25 { return 2 } // 9+16+0 13 if f32_int(f32_sub(f32_of(7), f32_of(2))) != 5 { return 3 } // 7-2 14 let out: *i64 = sys_mmap(24) as *i64 15 v3_cross(f32_of(1), f32_of(0), f32_of(0), f32_of(0), f32_of(1), f32_of(0), out) 16 if f32_int(out[0]) != 0 { return 4 } 17 if f32_int(out[1]) != 0 { return 5 } 18 if f32_int(out[2]) != 1 { return 6 } // x cross y = z 19 if f32_int(f32_div(f32_of(6), f32_of(2))) != 3 { return 7 } // 6/2 20 // signed control: negate one component -> 4+10-18 = -4 (proves real SIGNED float, not magnitude) 21 let dn: i64 = v3_dot(f32_of(1), f32_of(2), f32_neg(f32_of(3)), f32_of(4), f32_of(5), f32_of(6)) 22 if f32_int(dn) != (0 - 4) { return 8 } 23 24 // DIFFERENTIAL: hardware SSE f32 (__f32_*) must equal the independent i64 soft-float 25 // IEEE-754 (nx_f32.nx) BIT-FOR-BIT (same binary32 layout). Two implementations agreeing 26 // = no-false-green proof the hardware path is conformant, not just self-consistent. 27 let x: i64 = f32_of(6) 28 let y: i64 = f32_of(7) 29 if __f32_mul(x, y) != nx_f32_mul(x, y) { return 9 } // 42.0f, both paths identical bits 30 if __f32_add(x, y) != nx_f32_add(x, y) { return 10 } // 13.0f 31 let half: i64 = __f32_div(f32_of(1), f32_of(2)) // 0.5f (fractional) 32 if __f32_mul(half, x) != nx_f32_mul(half, x) { return 11 } // 3.0f 33 34 // mat4 KAT (the projection workhorse): identity*v = v; diag(2,3,4,1)*[1,1,1,1] = [2,3,4,1]; 35 // diag * identity = diag (exercises m4_mul). 36 let m: *i64 = sys_mmap(128) as *i64 37 let v: *i64 = sys_mmap(32) as *i64 38 let r4: *i64 = sys_mmap(32) as *i64 39 m4_identity(m) 40 v[0] = f32_of(1); v[1] = f32_of(2); v[2] = f32_of(3); v[3] = f32_of(4) 41 m4_vec4(m, v, r4) 42 if f32_int(r4[0]) != 1 { return 12 } 43 if f32_int(r4[1]) != 2 { return 13 } 44 if f32_int(r4[2]) != 3 { return 14 } 45 if f32_int(r4[3]) != 4 { return 15 } 46 m[0] = f32_of(2); m[5] = f32_of(3); m[10] = f32_of(4) // -> diag(2,3,4,1) 47 v[0] = f32_of(1); v[1] = f32_of(1); v[2] = f32_of(1); v[3] = f32_of(1) 48 m4_vec4(m, v, r4) 49 if f32_int(r4[0]) != 2 { return 16 } 50 if f32_int(r4[1]) != 3 { return 17 } 51 if f32_int(r4[2]) != 4 { return 18 } 52 if f32_int(r4[3]) != 1 { return 19 } 53 let mi: *i64 = sys_mmap(128) as *i64 54 let mp: *i64 = sys_mmap(128) as *i64 55 m4_identity(mi) 56 m4_mul(m, mi, mp) // diag * identity = diag 57 if f32_int(mp[0]) != 2 { return 20 } 58 if f32_int(mp[5]) != 3 { return 21 } 59 if f32_int(mp[10]) != 4 { return 22 } 60 61 // ===== IEEE-CLASS DIFFERENTIAL (added 2026-08-23) ========================= 62 // WHY THIS SECTION EXISTS. The differential above was TRUE and NARROW: it 63 // compared the two paths on three tame values (6, 7, 0.5) and could not have 64 // seen an edge-class divergence. An exhaustive 16x16 probe over every IEEE 65 // class (_offc_probe_f32equiv.nx) measured 254/256 agreeing BIT-FOR-BIT and 66 // exactly 2 differing -- so the narrow green was not wrong, it was blind. 67 // These teeth assert the classes ELEMENT-WISE, never as a count: a count can 68 // pass while the wrong members agree. 69 let z_p: i64 = 0 // +0 0x00000000 70 let z_n: i64 = 2147483648 // -0 0x80000000 71 let one: i64 = 1065353216 // 1.0 0x3F800000 72 let inf_p: i64 = 2139095040 // +inf 0x7F800000 73 let inf_n: i64 = 4286578688 // -inf 0xFF800000 74 let nan_q: i64 = 2143289344 // qNaN 0x7FC00000 75 let sub_lo: i64 = 1 // min subnormal 0x00000001 76 let sub_hi: i64 = 8388607 // max subnormal 0x007FFFFF 77 let nrm_lo: i64 = 8388608 // min normal 0x00800000 78 let nrm_hi: i64 = 2139095039 // max normal 0x7F7FFFFF 79 let ulp24: i64 = 855638016 // 2^-24 0x33000000 80 let ulp23: i64 = 872415232 // 2^-23 0x34000000 81 82 // Rounding-sensitive: these two REFUTE the double-rounding hazard. If OP_FADD 83 // lowered through f64 (the `addsd` observation) and round-tripped, 1.0+2^-24 and 84 // 1.0+2^-23 would diverge. They must not. 85 if __f32_add(one, ulp24) != nx_f32_add(one, ulp24) { return 23 } 86 if __f32_add(one, ulp23) != nx_f32_add(one, ulp23) { return 24 } 87 // Subnormals: the other place an f64 detour or a flush-to-zero would show. 88 if __f32_add(sub_lo, sub_lo) != nx_f32_add(sub_lo, sub_lo) { return 25 } 89 if __f32_add(sub_hi, sub_lo) != nx_f32_add(sub_hi, sub_lo) { return 26 } 90 if __f32_add(sub_hi, nrm_lo) != nx_f32_add(sub_hi, nrm_lo) { return 27 } 91 // Signed zeros: +0 + -0 must be +0 under round-to-nearest on BOTH paths. 92 if __f32_add(z_p, z_n) != nx_f32_add(z_p, z_n) { return 28 } 93 if __f32_add(z_n, z_n) != nx_f32_add(z_n, z_n) { return 29 } 94 // Overflow to infinity. 95 if __f32_add(nrm_hi, nrm_hi) != nx_f32_add(nrm_hi, nrm_hi) { return 30 } 96 // Infinity with a finite operand, and NaN propagation. 97 if __f32_add(inf_p, one) != nx_f32_add(inf_p, one) { return 31 } 98 if __f32_add(nan_q, one) != nx_f32_add(nan_q, one) { return 32 } 99 100 // ===== THE ONE DECLARED DIVERGENCE, AND THE ANTI-VACUITY TOOTH ============ 101 // inf + (-inf) is NaN on BOTH paths but they differ in the NaN SIGN BIT: 102 // the soft-float returns a fixed POSITIVE qNaN (NX_F32_NAN_RAW = 0x7FC00000); 103 // x86 returns its default qNaN, which is NEGATIVE (0xFFC00000). 104 // IEEE-754 DOES NOT SPECIFY THE SIGN OF A NaN RESULT, so neither is wrong -- 105 // this is a difference that must be DECLARED rather than DISCOVERED. 106 let d_sw: i64 = nx_f32_add(inf_p, inf_n) 107 let d_hw: i64 = __f32_add(inf_p, inf_n) 108 // Both must be NaN by CLASS. nx_f32_classify reads only the exponent and 109 // mantissa fields -- it never touches the sign bit -- so every consumer that 110 // detects NaN through the estate's own classifier is structurally blind to 111 // this divergence. That is what makes the difference safe, and it is asserted 112 // here rather than assumed. 113 if nx_f32_classify(d_sw) != NX_F32_CLS_NAN { return 33 } 114 if nx_f32_classify(d_hw) != NX_F32_CLS_NAN { return 34 } 115 // ANTI-VACUITY. The two arms must be GENUINELY DIFFERENT IMPLEMENTATIONS. 116 // This tooth is the proof: if someone re-points nx_f32_add's body at 117 // __f32_add (the pending library swap), the two arms collapse into one, this 118 // divergence vanishes, and THIS TOOTH GOES RED -- which is exactly the 119 // intended behaviour. The swap then cannot ship silently: it must come with a 120 // deliberate edit here, which is the mechanism that makes it DECLARED. 121 // Without this tooth, "the paths agree" would be indistinguishable from 122 // "there is only one path". 123 if d_sw == d_hw { return 35 } 124 if nx_f32_sign(d_sw) != 0 { return 36 } // soft-float NaN is positive 125 if nx_f32_sign(d_hw) != 1 { return 37 } // x86 default NaN is negative 126 127 return 0 128}