code wiki / (root) / nx_f64_sqrt.nx

nx_f64_sqrt.nx source

↩ module page · 94 lines · 3301 B

1// nx_f64_sqrt.nx -- IEEE 754 binary64 square root (standalone file). 2// 3// Digit-pair (2-bits-per-iteration) restoring square root on the radicand 4// sig * 2^54, fed MSB-first as 54 bit-pairs (27 pairs from the 54-bit 5// adjusted significand, then 27 zero pairs). Produces a 54-bit root = 6// 53-bit mantissa + explicit guard; sticky = remainder nonzero. Correctly 7// rounded RNE for all finite positive inputs including subnormals. 8// 9// Bounds: root < 2^54, trial = (root<<2)|1 < 2^56, rem <= 2*root + pair 10// so rem << 2 < 2^57 -- all comfortably inside i64. 11// 12// IEEE specials: sqrt(+0) = +0, sqrt(-0) = -0, sqrt(+inf) = +inf, 13// sqrt(negative) = NaN, sqrt(NaN) = NaN. 14// 15// license_tier: ORIGINAL 16 17import "nx_syscalls.nx" 18import "nx_tier.nx" 19import "nx_f64.nx" 20const K_MAGIC_1049: i64 = 1049 21 22func nx_f64_sqrt(a: i64) -> i64 { 23 let cls_a: nx_int = nx_f64_classify(a) 24 25 if cls_a == NX_F64_CLS_NAN { return NX_F64_NAN_RAW } 26 if cls_a == NX_F64_CLS_ZERO { return a } // preserves -0 27 if nx_f64_sign(a) == 1 { return NX_F64_NAN_RAW } // negative -> NaN 28 if cls_a == NX_F64_CLS_INF { return a } // +inf 29 30 // Pre-normalize significand to [2^52, 2^53). 31 var sig: i64 = nx_f64_mant_field(a) 32 var ef: i64 = nx_f64_exp_field(a) 33 if ef == 0 { 34 ef = 1 35 while sig < NX_F64_IMPLICIT_1 { sig = sig << 1; ef = ef - 1 } 36 } else { 37 sig = sig | NX_F64_IMPLICIT_1 38 } 39 40 // value = sig * 2^(E - 52), E unbiased. Make E even so the exponent 41 // halves cleanly; the odd case doubles sig (now < 2^54). 42 var e_unb: i64 = ef - NX_F64_EXP_BIAS 43 // Parity via bit 0 (two's complement keeps bit 0 = parity for negatives; 44 // E - 2*(E/2) would be wrong there under truncating division). 45 let parity: i64 = e_unb & 1 46 if parity != 0 { 47 sig = sig << 1 48 e_unb = e_unb - 1 49 } 50 // Now E even; sqrt(value) = sqrt(sig) * 2^((E-52)/2)... computed as 51 // root = floor(sqrt(sig * 2^54)) (in [2^53, 2^54)), giving 52 // sqrt(value) = (root + frac) * 2^(F - 27) with F = (E - 52) / 2. 53 // E - 52 is even and may be negative; arithmetic >> 1 halves correctly. 54 let f_half: i64 = (e_unb - 52) >> 1 55 56 // Digit-pair restoring sqrt: 27 pairs from sig (54 bits), 27 zero pairs. 57 var rem: i64 = 0 58 var root: i64 = 0 59 var idx: i64 = 26 60 while idx >= 0 { 61 let pair: i64 = (sig >> (idx * 2)) & 3 62 rem = (rem << 2) | pair 63 let trial: i64 = (root << 2) | 1 64 if rem >= trial { 65 rem = rem - trial 66 root = (root << 1) | 1 67 } else { 68 root = root << 1 69 } 70 idx = idx - 1 71 } 72 var j: i64 = 0 73 while j < 27 { 74 rem = rem << 2 75 let trial2: i64 = (root << 2) | 1 76 if rem >= trial2 { 77 rem = rem - trial2 78 root = (root << 1) | 1 79 } else { 80 root = root << 1 81 } 82 j = j + 1 83 } 84 85 // root has 54 bits: mantissa 53 + guard 1. value = root * 2^(f_half-27). 86 let mant_out: i64 = root >> 1 87 let guard: i64 = root & 1 88 var sticky: i64 = 0 89 if rem != 0 { sticky = 1 } 90 // mant * 2^(eo-1075) = root/2 * 2^(eo-1075) => eo = f_half + 1049. 91 let eo: i64 = f_half + K_MAGIC_1049 92 93 return _f64_round_pack(0, eo, mant_out, guard, sticky) 94}