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}