Skip to main content

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);