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}