machina_softfloat/ops/sqrt.rs
1// SPDX-License-Identifier: MIT
2// IEEE 754 floating-point square root.
3
4use crate::env::FloatEnv;
5use crate::parts::{
6 nan_propagate_one, return_nan, round_pack, unpack, FloatClass, FloatParts,
7};
8use crate::types::{
9 BFloat16, Float128, Float16, Float32, Float64, FloatFormat, FloatX80,
10};
11
12const INT_BIT: u32 = 126;
13
14/// Floating-point square root.
15pub fn sqrt<F: FloatFormat>(a: F, env: &mut FloatEnv) -> F {
16 let pa = unpack::<F>(a);
17
18 if pa.is_nan() {
19 let mut r = nan_propagate_one(&pa, env);
20 return round_pack::<F>(&mut r, env);
21 }
22
23 if pa.cls == FloatClass::Inf {
24 if pa.sign {
25 // sqrt(-Inf) = NaN, INVALID
26 return return_nan::<F>(env);
27 }
28 let mut r = pa;
29 return round_pack::<F>(&mut r, env);
30 }
31
32 if pa.cls == FloatClass::Zero {
33 // sqrt(+-0) = +-0
34 let mut r = pa;
35 return round_pack::<F>(&mut r, env);
36 }
37
38 // sqrt of negative number = NaN, INVALID
39 if pa.sign {
40 return return_nan::<F>(env);
41 }
42
43 // Normal positive number.
44 // Compute sqrt using Newton-Raphson on the u128 mantissa.
45 //
46 // If exp is odd, shift frac right by 1 so that exp
47 // becomes even (we need exp/2 to be an integer).
48 let mut frac = pa.frac;
49 let mut exp = pa.exp;
50
51 if exp & 1 != 0 {
52 frac >>= 1;
53 exp += 1;
54 }
55
56 // Result exponent is exp/2.
57 let result_exp = exp >> 1;
58
59 // Compute integer square root of frac.
60 // frac has integer bit at position 126 (or 125 after
61 // the odd-exp shift). The result should have the
62 // integer bit at position 126.
63 //
64 // sqrt(frac) where frac ~ 2^126 -> result ~ 2^63.
65 // We need to scale: result = sqrt(frac) << 63 (approx).
66 //
67 // Better: compute sqrt(frac << 126) to get a result
68 // with ~126 significant bits. But we can't shift a
69 // u128 by 126.
70 //
71 // Alternative: bit-by-bit sqrt algorithm.
72 let result_frac = isqrt_u128(frac);
73
74 let mut result = FloatParts {
75 sign: false,
76 exp: result_exp,
77 frac: result_frac,
78 cls: FloatClass::Normal,
79 };
80 round_pack::<F>(&mut result, env)
81}
82
83/// Integer square root with extra precision for rounding.
84/// Input has the integer bit at position 126.
85/// Output has the integer bit at position 126.
86///
87/// We compute sqrt(frac * 2^126), then the result has the
88/// integer bit at position 126 of the output.
89///
90/// Since frac ~ 2^126, sqrt(frac) ~ 2^63. We need 2^126
91/// in the result, so we compute: result = sqrt(frac) << 63.
92/// But we need more precision than that for rounding.
93///
94/// Use the bit-by-bit square root algorithm operating on
95/// a 256-bit extended value (frac << 126) to produce 128
96/// quotient bits.
97fn isqrt_u128(frac: u128) -> u128 {
98 // We want to compute floor(sqrt(frac * 2^126)).
99 // This gives us a result with integer bit at position
100 // 126 (since sqrt(2^126 * 2^126) = 2^126).
101 //
102 // Actually: frac has integer bit at position 126, so
103 // frac ~ 1.xxx * 2^126. Then frac * 2^126 ~ 2^252.
104 // sqrt(2^252) = 2^126. Good.
105 //
106 // We can't represent 2^252 in a u128. So we use the
107 // digit-by-digit method with a virtual shift.
108
109 // Simplified approach: use Newton's method with u128.
110 // Start with an estimate and iterate.
111
112 if frac == 0 {
113 return 0;
114 }
115
116 let mut rem: u128 = 0;
117 let mut result: u128 = 0;
118
119 // We process 2 bits of the radicand per iteration
120 // to produce 1 bit of the result.
121 // Total result bits needed: 128 (position 0..127).
122 // Total radicand bits: 253 (positions 0..252).
123 // We process from the top.
124
125 // The radicand is (frac << 126). Its bits:
126 // - bits [252..126] come from frac[126..0]
127 // - bits [125..0] are all zero
128
129 for i in (0..=INT_BIT).rev() {
130 // We're producing result bit at position
131 // (INT_BIT - (INT_BIT - i)) = i... let me
132 // restructure.
133 // Actually, let's produce bits from position
134 // INT_BIT down to 0 (127 iterations).
135 let bit_pos = i;
136
137 // Bring in 2 bits of the radicand.
138 // Radicand bit positions for iteration producing
139 // result bit at position `bit_pos`:
140 // radicand bits at 2*bit_pos+1 and 2*bit_pos.
141 let rb_hi = 2 * bit_pos + 1;
142 let rb_lo = 2 * bit_pos;
143
144 // Get radicand bits from (frac << 126).
145 let get_radicand_bit = |pos: u32| -> u128 {
146 if pos >= 253 {
147 return 0;
148 }
149 if pos >= 126 {
150 (frac >> (pos - 126)) & 1
151 } else {
152 0 // lower 126 bits are zero
153 }
154 };
155
156 rem = (rem << 2)
157 | (get_radicand_bit(rb_hi) << 1)
158 | get_radicand_bit(rb_lo);
159
160 let trial = (result << 2) | 1;
161 if rem >= trial {
162 rem -= trial;
163 result = (result << 1) | 1;
164 } else {
165 result <<= 1;
166 }
167 }
168
169 // Set sticky bit if remainder is non-zero.
170 if rem != 0 {
171 result |= 1;
172 }
173
174 result
175}
176
177// ---------------------------------------------------------------
178// Convenience methods
179// ---------------------------------------------------------------
180
181macro_rules! impl_sqrt {
182 ($ty:ty) => {
183 impl $ty {
184 pub fn sqrt(self, env: &mut FloatEnv) -> Self {
185 sqrt::<Self>(self, env)
186 }
187 }
188 };
189}
190
191impl_sqrt!(Float16);
192impl_sqrt!(BFloat16);
193impl_sqrt!(Float32);
194impl_sqrt!(Float64);
195impl_sqrt!(Float128);
196impl_sqrt!(FloatX80);