nx_f32_div.nx source
↩ module page · 117 lines · 3774 B
1// nx_f32_div.nx -- IEEE 754 binary32 division (standalone).
2//
3// Separated from runtime/nx_f32.nx because having division code in
4// the same file as nx_f32_mul triggers the nxc2 codegen quirk
5// tracked in Task #10 (negative-literal sign-extension regression
6// in earlier mul KAT after certain patterns added). Isolating this
7// brick in its own file sidesteps the quirk while preserving the
8// L4 bits-up tower's contract.
9//
10// Composes:
11// nx_f32.nx -- classify / sign / exp_field / mant_field / constants
12//
13// Algorithm:
14// 1. NaN/Inf/Zero special cases per IEEE 754.
15// 2. Build 24-bit significands.
16// 3. (a_sig << 24) / b_sig gives 24- or 25-bit quotient.
17// 4. Normalize to 24-bit mantissa, extract guard from dropped bit,
18// sticky from remainder.
19// 5. Round-to-nearest-even.
20// 6. Overflow → inf, underflow → subnormal/zero.
21//
22// v1 limitations: subnormal input/output conservatively zero/inf.
23
24import "nx_syscalls.nx"
25import "nx_tier.nx"
26import "nx_f32.nx"
27
28func nx_f32_div(a: i64, b: i64) -> i64 {
29 let cls_a: nx_int = nx_f32_classify(a)
30 let cls_b: nx_int = nx_f32_classify(b)
31 let sign_out: i64 = nx_f32_sign(a) ^ nx_f32_sign(b)
32
33 if cls_a == NX_F32_CLS_NAN { return NX_F32_NAN_RAW }
34 if cls_b == NX_F32_CLS_NAN { return NX_F32_NAN_RAW }
35
36 if cls_b == NX_F32_CLS_ZERO {
37 if cls_a == NX_F32_CLS_ZERO { return NX_F32_NAN_RAW }
38 return (sign_out << 31) | NX_F32_INF_RAW
39 }
40 if cls_b == NX_F32_CLS_INF {
41 if cls_a == NX_F32_CLS_INF { return NX_F32_NAN_RAW }
42 return sign_out << 31
43 }
44
45 if cls_a == NX_F32_CLS_ZERO { return sign_out << 31 }
46 if cls_a == NX_F32_CLS_INF {
47 return (sign_out << 31) | NX_F32_INF_RAW
48 }
49
50 if cls_a == NX_F32_CLS_SUBNORMAL { return sign_out << 31 }
51 if cls_b == NX_F32_CLS_SUBNORMAL {
52 return (sign_out << 31) | NX_F32_INF_RAW
53 }
54
55 let mant_a: i64 = nx_f32_mant_field(a)
56 let exp_a: i64 = nx_f32_exp_field(a)
57 let sig_a: i64 = mant_a | NX_F32_IMPLICIT_1
58
59 let mant_b: i64 = nx_f32_mant_field(b)
60 let exp_b: i64 = nx_f32_exp_field(b)
61 let sig_b: i64 = mant_b | NX_F32_IMPLICIT_1
62
63 var exp_out: i64 = exp_a - exp_b + NX_F32_EXP_BIAS
64
65 let dividend: i64 = sig_a << 24
66 let q: i64 = dividend / sig_b
67 let rem: i64 = dividend - q * sig_b
68
69 // Case A (sig_a >= sig_b): q in [2^24, 2^25); shift right 1 for
70 // 24-bit mantissa, exp_out UNCHANGED (real ratio in [1,2)).
71 // Case B (sig_a < sig_b): q in [2^23, 2^24); already 24 bits, but
72 // real ratio in [0.5,1) so DECREMENT exp_out by 1 to keep
73 // canonical implicit-1 form.
74 var mant_out: i64 = 0
75 var guard_bit: i64 = 0
76 var sticky: i64 = 0
77 if sig_a >= sig_b {
78 mant_out = q >> 1
79 guard_bit = q & 1
80 if rem != 0 { sticky = 1 }
81 } else {
82 mant_out = q
83 guard_bit = 0
84 if rem != 0 { sticky = 1 }
85 exp_out = exp_out - 1
86 }
87
88 var round_up: i64 = 0
89 if guard_bit == 1 {
90 if sticky == 1 { round_up = 1 }
91 if sticky == 0 {
92 if (mant_out & 1) == 1 { round_up = 1 }
93 }
94 }
95 if round_up == 1 {
96 mant_out = mant_out + 1
97 if mant_out >= 0x01000000 {
98 mant_out = mant_out >> 1
99 exp_out = exp_out + 1
100 }
101 }
102
103 if exp_out >= 255 {
104 return (sign_out << 31) | NX_F32_INF_RAW
105 }
106 if exp_out <= 0 {
107 let shifts: i64 = 1 - exp_out
108 if shifts > 24 {
109 return sign_out << 31
110 }
111 mant_out = mant_out >> shifts
112 return (sign_out << 31) | (mant_out & NX_F32_MANT_MASK)
113 }
114
115 let mant_final: i64 = mant_out & NX_F32_MANT_MASK
116 return (sign_out << 31) | (exp_out << NX_F32_EXP_SHIFT) | mant_final
117}