code wiki / (root) / nx_f64_adversary.nx

nx_f64_adversary.nx source

↩ module page · 40 lines · 2138 B

1// nx_f64_adversary.nx -- T12 class: hardware f64 (IEEE-754 binary64) codegen. 2// Before 2026-07-16 nx_cc PARSED f64 but SILENTLY MISCOMPILED f64 arithmetic to 3// single precision (the parser forced every fp binop result to TY_F32, so the 4// emitter used movss/divss -> low-32-bit reinterpret -> garbage). Fixed by 5// fp_result_type (TY_F64 propagation) + x86ctx_emit_f64 (movsd/addsd/subsd/ 6// mulsd/divsd/sqrtsd/cvtsi2sd/cvttsd2si) + the __f64_from_i64/__f64_to_i64/ 7// __f64_sqrt intrinsics. Self-checking (exit 0 = all f64 ops bit-correct); every 8// constant is a known IEEE-754 value truncated to a fixed integer scale, so a 9// single-precision regression (or a wrong conversion) changes the digits. 10// license_tier: ORIGINAL No hw writes (Rule 26). 11import "nx_syscalls_x86_64.nx" 12 13func main() -> i64 { 14 // division: 1/3 to 9 digits. f32 would give 333333343-ish, not 333333333. 15 let third: f64 = 1.0 / 3.0 16 if __f64_to_i64(third * 1000000000.0) != 333333333 { return 1 } 17 // sqrt: sqrt(2) to 9 digits = 1.414213562. 18 if __f64_to_i64(__f64_sqrt(2.0) * 1000000000.0) != 1414213562 { return 2 } 19 // int -> f64 -> divide: 7/2 = 3.5. 20 let seven: f64 = __f64_from_i64(7) 21 if __f64_to_i64(seven / 2.0 * 1000000.0) != 3500000 { return 3 } 22 // subtraction: 5.0 - 1.5 = 3.5. 23 if __f64_to_i64((5.0 - 1.5) * 1000000.0) != 3500000 { return 4 } 24 // multiply-accumulate chain (spectral-norm's inner shape): sum += a*b. 25 var sum: f64 = 0.0 26 var k: i64 = 1 27 while k <= 100 { 28 // 1/(k*k) summed -> pi^2/6 partial; check to 6 digits at k=100. 29 let kf: f64 = __f64_from_i64(k) 30 sum = sum + 1.0 / (kf * kf) 31 k = k + 1 32 } 33 // sum_{1..100} 1/k^2 = 1.634983900... -> *1e6 truncated = 1634983. 34 if __f64_to_i64(sum * 1000000.0) != 1634983 { return 5 } 35 // f64 precision witness: 0.1 + 0.2 != 0.3 exactly, but *1e15 truncates to a 36 // STABLE binary64 value (300000000000000 with the classic +4 ULP tail gone 37 // at 1e15 scale). f32 would diverge earlier. Use a coarser 1e9 check. 38 if __f64_to_i64((0.1 + 0.2) * 1000000000.0) != 300000000 { return 6 } 39 return 0 40}