code wiki / (root) / nx_f32_div.nx

nx_f32_div.nx source

↩ module page · 121 lines · 4343 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 // ONE EXTRA QUOTIENT BIT (root fix 2026-09-14): the v1 body computed only 24 quotient bits, so in Case B the 66 // guard bit was set to a CONSTANT 0 and every such division was TRUNCATED, never rounded to nearest even. 67 // nx_f32_bricks_hw_gate measured it against the hardware divide: 937 of 4096 random pairs off the IEEE result 68 // while add and mul matched bit for bit. With sig_a << 25 the guard is a real quotient bit in both cases. 69 let dividend: i64 = sig_a << 25 70 let q: i64 = dividend / sig_b 71 let rem: i64 = dividend - q * sig_b 72 73 // Case A (sig_a >= sig_b): q in [2^25, 2^26); mantissa = q >> 2, guard = bit 1, sticky = bit 0 or remainder, 74 // exp_out UNCHANGED (real ratio in [1,2)). 75 // Case B (sig_a < sig_b): q in [2^24, 2^25); mantissa = q >> 1, guard = bit 0, sticky = remainder, and the 76 // real ratio is in [0.5,1) so DECREMENT exp_out by 1 to keep canonical implicit-1 form. 77 var mant_out: i64 = 0 78 var guard_bit: i64 = 0 79 var sticky: i64 = 0 80 if sig_a >= sig_b { 81 mant_out = q >> 2 82 guard_bit = (q >> 1) & 1 83 if (q & 1) != 0 { sticky = 1 } 84 if rem != 0 { sticky = 1 } 85 } else { 86 mant_out = q >> 1 87 guard_bit = q & 1 88 if rem != 0 { sticky = 1 } 89 exp_out = exp_out - 1 90 } 91 92 var round_up: i64 = 0 93 if guard_bit == 1 { 94 if sticky == 1 { round_up = 1 } 95 if sticky == 0 { 96 if (mant_out & 1) == 1 { round_up = 1 } 97 } 98 } 99 if round_up == 1 { 100 mant_out = mant_out + 1 101 if mant_out >= 0x01000000 { 102 mant_out = mant_out >> 1 103 exp_out = exp_out + 1 104 } 105 } 106 107 if exp_out >= 255 { 108 return (sign_out << 31) | NX_F32_INF_RAW 109 } 110 if exp_out <= 0 { 111 let shifts: i64 = 1 - exp_out 112 if shifts > 24 { 113 return sign_out << 31 114 } 115 mant_out = mant_out >> shifts 116 return (sign_out << 31) | (mant_out & NX_F32_MANT_MASK) 117 } 118 119 let mant_final: i64 = mant_out & NX_F32_MANT_MASK 120 return (sign_out << 31) | (exp_out << NX_F32_EXP_SHIFT) | mant_final 121}