nx_exp_ab.nx source
↩ module page · 79 lines · 3405 B
1// nx_exp_ab.nx -- same-process A/B of the exp hot path: the OLD software
2// nx_f32_mul/add polynomial vs the NEW hardware __f32 one (now IN
3// nx_f32_exp). A = a local software-op copy of the poly; B = nx_f32_exp
4// (hardware). Same inputs, back-to-back -> noise-immune RATIO + a
5// bit-exactness check (must be 0 mismatches). license_tier: ORIGINAL
6// expect_exit: 0
7import "nx_syscalls.nx"
8import "nx_tier.nx"
9import "nx_f32.nx"
10import "nx_f32_div.nx"
11import "nx_f32_cvt.nx"
12import "nx_f32_exp.nx"
13import "nx_fmt.nx"
14const K_MAGIC_2000000: i64 = 2000000
15const K_MAGIC_8192: i64 = 8192
16const K_MAGIC_4096: i64 = 4096
17
18func eab_nl() -> i64 { fmt_puts("\n" as *u8); return 0 }
19
20// software-op copy of the exp poly (the pre-2026-07-10 hot path), to time
21// against the shipped hardware nx_f32_exp. Same range-reduce + 6-term.
22func exp_soft(x: i64) -> i64 {
23 let cls: nx_int = nx_f32_classify(x)
24 if cls == NX_F32_CLS_NAN { return NX_F32_NAN_RAW }
25 if cls == NX_F32_CLS_ZERO { return NX_F32_ONE }
26 if cls == NX_F32_CLS_INF { if nx_f32_sign(x) == 1 { return 0 } return 0x7F800000 }
27 let sx: i64 = nx_f32_sign(x)
28 let ax: i64 = x & 0x7FFFFFFF
29 if sx == 0 { if ax > NX_F32_OVF_THRESH { return 0x7F800000 } }
30 else { if ax > (NX_F32_UNF_THRESH & 0x7FFFFFFF) { return 0 } }
31 let y: i64 = nx_f32_mul(x, NX_F32_INV_LN2)
32 let k: i64 = _f32_to_i32_rne(y)
33 let kf: i64 = nx_i32_to_f32(k)
34 let r: i64 = nx_f32_sub(x, nx_f32_mul(kf, NX_F32_LN2))
35 let h6: i64 = nx_f32_add(NX_F32_ONE, nx_f32_mul(r, NX_F32_INV_6))
36 let h5: i64 = nx_f32_add(NX_F32_ONE, nx_f32_mul(nx_f32_mul(r, NX_F32_INV_5), h6))
37 let h4: i64 = nx_f32_add(NX_F32_ONE, nx_f32_mul(nx_f32_mul(r, NX_F32_INV_4), h5))
38 let h3: i64 = nx_f32_add(NX_F32_ONE, nx_f32_mul(nx_f32_mul(r, NX_F32_INV_3), h4))
39 let h2: i64 = nx_f32_add(NX_F32_ONE, nx_f32_mul(nx_f32_mul(r, NX_F32_INV_2), h3))
40 let er: i64 = nx_f32_add(NX_F32_ONE, nx_f32_mul(r, h2))
41 return _f32_ldexp(er, k)
42}
43
44func main() -> i64 {
45 let N: i64 = K_MAGIC_2000000
46 // bit-exactness sweep over a representative range (-8..8 in /512 steps).
47 var mism: i64 = 0
48 var i: i64 = 0
49 while i < K_MAGIC_8192 {
50 let xv: i64 = nx_f32_div(nx_i32_to_f32(i - K_MAGIC_4096), nx_i32_to_f32(512))
51 if exp_soft(xv) != nx_f32_exp(xv) { mism = mism + 1 }
52 i = i + 1
53 }
54 fmt_puts("BITEXACT soft==hw mismatches="); fmt_putn(mism); eab_nl()
55 if mism != 0 { fmt_puts("FATAL hw exp diverges"); eab_nl(); return 77 }
56
57 let xv: i64 = nx_f32_div(nx_i32_to_f32(0 - 3), nx_i32_to_f32(2)) // -1.5
58 // A: software
59 let ta: i64 = sys_now_us()
60 var acc: i64 = 0
61 var a: i64 = 0
62 while a < N { acc = acc ^ exp_soft(xv); a = a + 1 }
63 let usa: i64 = sys_now_us() - ta
64 // B: hardware
65 let tb: i64 = sys_now_us()
66 var acc2: i64 = 0
67 var b: i64 = 0
68 while b < N { acc2 = acc2 ^ nx_f32_exp(xv); b = b + 1 }
69 let usb: i64 = sys_now_us() - tb
70
71 fmt_puts("A(soft) us="); fmt_putn(usa); fmt_puts(" ns/exp="); fmt_putn(usa * 1000 / N); eab_nl()
72 fmt_puts("B(hw) us="); fmt_putn(usb); fmt_puts(" ns/exp="); fmt_putn(usb * 1000 / N); eab_nl()
73 var ub: i64 = usb
74 if ub < 1 { ub = 1 }
75 fmt_puts("soft_over_hw_x100="); fmt_putn(usa * 100 / ub); eab_nl()
76 if acc == acc2 { fmt_puts("(acc guard)"); eab_nl() } // keep both live
77 fmt_puts("EXP_AB DONE"); eab_nl()
78 return 0
79}