code wiki / (root) / nx_f32_div.nx

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}