code wiki / (root) / nx_f64_cvt.nx

nx_f64_cvt.nx source

↩ module page · 156 lines · 5171 B

1// nx_f64_cvt.nx -- conversions to/from IEEE 754 binary64. 2// 3// v1: nx_i64_to_f64 (exact for |v| < 2^53, correctly rounded RNE beyond) 4// nx_f64_to_f32 (narrowing with RNE, subnormal + overflow correct) 5// 6// license_tier: ORIGINAL 7 8import "nx_syscalls.nx" 9import "nx_tier.nx" 10import "nx_f64.nx" 11 12func nx_i64_to_f64(v: i64) -> i64 { 13 if v == 0 { return 0 } 14 var sign: i64 = 0 15 var m: i64 = v 16 if v < 0 { 17 sign = 1 18 if v == (1 << 63) { 19 // |i64 min| overflows negation; -2^63 is exactly representable. 20 return 0xC3E0000000000000 21 } 22 m = 0 - v 23 } 24 // Find the most significant bit position k. 25 var k: i64 = 0 26 var probe: i64 = m 27 while probe > 1 { probe = probe >> 1; k = k + 1 } 28 29 if k <= 52 { 30 // Exact: left-justify into the 53-bit significand. 31 let mant: i64 = m << (52 - k) 32 let eo: i64 = NX_F64_EXP_BIAS + k 33 return _f64_round_pack(sign, eo, mant, 0, 0) 34 } 35 // k in [53, 62]: shift right, capture guard + sticky. 36 let drop: i64 = k - 52 37 let mant2: i64 = m >> drop 38 let guard: i64 = (m >> (drop - 1)) & 1 39 var sticky: i64 = 0 40 if drop >= 2 { 41 if (m & ((1 << (drop - 1)) - 1)) != 0 { sticky = 1 } 42 } 43 return _f64_round_pack(sign, NX_F64_EXP_BIAS + k, mant2, guard, sticky) 44} 45 46// f64 -> i64, truncation toward zero. NaN/inf return 0 / saturate; values 47// past +-2^62 saturate (callers in the transcendental family guard range 48// before calling, so saturation is a never-hit backstop, not a contract). 49func nx_f64_to_i64(raw: i64) -> i64 { 50 let cls: nx_int = nx_f64_classify(raw) 51 if cls == NX_F64_CLS_ZERO { return 0 } 52 if cls == NX_F64_CLS_NAN { return 0 } 53 let sign: i64 = nx_f64_sign(raw) 54 if cls == NX_F64_CLS_INF { 55 if sign == 1 { return 0 - 0x4000000000000000 } 56 return 0x4000000000000000 57 } 58 if cls == NX_F64_CLS_SUBNORMAL { return 0 } 59 let ef: i64 = nx_f64_exp_field(raw) 60 if ef < NX_F64_EXP_BIAS { return 0 } // |x| < 1 61 let sig: i64 = nx_f64_mant_field(raw) | NX_F64_IMPLICIT_1 62 let shift: i64 = ef - NX_F64_EXP_BIAS - 52 63 var m: i64 = 0 64 if shift >= 0 { 65 if shift > 9 { 66 // |x| >= 2^62: saturate. 67 if sign == 1 { return 0 - 0x4000000000000000 } 68 return 0x4000000000000000 69 } 70 m = sig << shift 71 } else { 72 let down: i64 = 0 - shift 73 if down > 52 { return 0 } 74 m = sig >> down 75 } 76 if sign == 1 { return 0 - m } 77 return m 78} 79 80// Exact scaling by 2^k with correct subnormal rounding and overflow -> inf 81// (composes _f64_round_pack; sig pre-normalized so eo algebra is uniform). 82func nx_f64_ldexp(raw: i64, k: i64) -> i64 { 83 let cls: nx_int = nx_f64_classify(raw) 84 if cls == NX_F64_CLS_ZERO { return raw } 85 if cls == NX_F64_CLS_NAN { return raw } 86 if cls == NX_F64_CLS_INF { return raw } 87 let sign: i64 = nx_f64_sign(raw) 88 var sig: i64 = nx_f64_mant_field(raw) 89 var ef: i64 = nx_f64_exp_field(raw) 90 if ef == 0 { 91 ef = 1 92 while sig < NX_F64_IMPLICIT_1 { sig = sig << 1; ef = ef - 1 } 93 } else { 94 sig = sig | NX_F64_IMPLICIT_1 95 } 96 // Clamp k so ef + k stays in i64-safe territory (round_pack handles the rest). 97 var kk: i64 = k 98 if kk > 4096 { kk = 4096 } 99 if kk < (0 - 4096) { kk = 0 - 4096 } 100 return _f64_round_pack(sign, ef + kk, sig, 0, 0) 101} 102 103// Narrow binary64 -> binary32 (result in low 32 bits of i64, f32 convention). 104func nx_f64_to_f32(raw: i64) -> i64 { 105 let cls: nx_int = nx_f64_classify(raw) 106 let sign: i64 = nx_f64_sign(raw) 107 if cls == NX_F64_CLS_NAN { return 0x7FC00000 } 108 if cls == NX_F64_CLS_INF { return (sign << 31) | 0x7F800000 } 109 if cls == NX_F64_CLS_ZERO { return sign << 31 } 110 111 var sig: i64 = nx_f64_mant_field(raw) 112 var ef: i64 = nx_f64_exp_field(raw) 113 if ef == 0 { 114 ef = 1 115 while sig < NX_F64_IMPLICIT_1 { sig = sig << 1; ef = ef - 1 } 116 } else { 117 sig = sig | NX_F64_IMPLICIT_1 118 } 119 // f32 biased exponent for the same value. 120 var eo: i64 = ef - NX_F64_EXP_BIAS + 127 121 // 53-bit sig -> 24-bit sig: drop 29 bits with guard + sticky. 122 var mant: i64 = sig >> 29 123 var guard: i64 = (sig >> 28) & 1 124 var sticky: i64 = 0 125 if (sig & ((1 << 28) - 1)) != 0 { sticky = 1 } 126 127 // Denormalize for f32 subnormals before rounding. 128 if eo < 1 { 129 let k: i64 = 1 - eo 130 if k > 25 { return sign << 31 } 131 var i: i64 = 0 132 while i < k { 133 if guard == 1 { sticky = 1 } 134 guard = mant & 1 135 mant = mant >> 1 136 i = i + 1 137 } 138 eo = 1 139 } 140 var round_up: i64 = 0 141 if guard == 1 { 142 if sticky == 1 { round_up = 1 } 143 if round_up == 0 { 144 if (mant & 1) == 1 { round_up = 1 } 145 } 146 } 147 if round_up == 1 { 148 mant = mant + 1 149 if mant >= 0x01000000 { mant = mant >> 1; eo = eo + 1 } 150 } 151 if eo >= 255 { 152 if mant >= 0x00800000 { return (sign << 31) | 0x7F800000 } 153 } 154 if mant < 0x00800000 { return (sign << 31) | mant } 155 return (sign << 31) | (eo << 23) | (mant & 0x007FFFFF) 156}