Skip to main content

c/
math_sqrtfmod.rs

1// Copyright (c) 2026 Meng Zhuo
2// SPDX-License-Identifier: BSD-3-Clause
3
4// Translated from musl: sqrt.c, sqrtf.c, fmod.c, fmodf.c, sqrt_data.c
5// Uses only integer/bit ops and f64::from_bits/to_bits.
6// No Rust float methods except final return.
7
8// ============================================================
9// sqrt_data: __rsqrt_tab lookup table
10// ============================================================
11
12const RSQRT_TAB: [u16; 128] = [
13    0xb451,0xb2f0,0xb196,0xb044,0xaef9,0xadb6,0xac79,0xab43,
14    0xaa14,0xa8eb,0xa7c8,0xa6aa,0xa592,0xa480,0xa373,0xa26b,
15    0xa168,0xa06a,0x9f70,0x9e7b,0x9d8a,0x9c9d,0x9bb5,0x9ad1,
16    0x99f0,0x9913,0x983a,0x9765,0x9693,0x95c4,0x94f8,0x9430,
17    0x936b,0x92a9,0x91ea,0x912e,0x9075,0x8fbe,0x8f0a,0x8e59,
18    0x8daa,0x8cfe,0x8c54,0x8bac,0x8b07,0x8a64,0x89c4,0x8925,
19    0x8889,0x87ee,0x8756,0x86c0,0x862b,0x8599,0x8508,0x8479,
20    0x83ec,0x8361,0x82d8,0x8250,0x81c9,0x8145,0x80c2,0x8040,
21    0xff02,0xfd0e,0xfb25,0xf947,0xf773,0xf5aa,0xf3ea,0xf234,
22    0xf087,0xeee3,0xed47,0xebb3,0xea27,0xe8a3,0xe727,0xe5b2,
23    0xe443,0xe2dc,0xe17a,0xe020,0xdecb,0xdd7d,0xdc34,0xdaf1,
24    0xd9b3,0xd87b,0xd748,0xd61a,0xd4f1,0xd3cd,0xd2ad,0xd192,
25    0xd07b,0xcf69,0xce5b,0xcd51,0xcc4a,0xcb48,0xca4a,0xc94f,
26    0xc858,0xc764,0xc674,0xc587,0xc49d,0xc3b7,0xc2d4,0xc1f4,
27    0xc116,0xc03c,0xbf65,0xbe90,0xbdbe,0xbcef,0xbc23,0xbb59,
28    0xba91,0xb9cc,0xb90a,0xb84a,0xb78c,0xb6d0,0xb617,0xb560,
29];
30
31// ============================================================
32// sqrt helpers
33// ============================================================
34
35#[inline]
36fn mul32(a: u32, b: u32) -> u32 {
37    ((a as u64 * b as u64) >> 32) as u32
38}
39
40#[inline]
41fn mul64(a: u64, b: u64) -> u64 {
42    let ahi = a >> 32;
43    let alo = a & 0xffffffff;
44    let bhi = b >> 32;
45    let blo = b & 0xffffffff;
46    ahi * bhi + (ahi * blo >> 32) + (alo * bhi >> 32)
47}
48
49// ============================================================
50// sqrt
51// ============================================================
52
53#[cfg(target_arch = "x86_64")]
54#[no_mangle]
55pub extern "C" fn sqrt(x: f64) -> f64 {
56    // Hardware sqrt respects the current rounding mode and raises the same
57    // INVALID flag as musl's x86_64 sqrt.s, but avoids the generic software
58    // algorithm (which is ~5x slower here).
59    let y: f64;
60    unsafe {
61        core::arch::asm!(
62            "sqrtsd {y}, {x}",
63            y = out(xmm_reg) y,
64            x = in(xmm_reg) x,
65            options(nomem, nostack)
66        );
67    }
68    y
69}
70
71#[cfg(not(target_arch = "x86_64"))]
72#[no_mangle]
73pub extern "C" fn sqrt(x: f64) -> f64 {
74    let ix = asuint64(x);
75    let mut top = ix >> 52;
76
77    // special case handling
78    if predict_false(top.wrapping_sub(0x001) >= 0x7ff - 0x001) {
79        // x < 0x1p-1022 or inf or nan
80        if ix.wrapping_mul(2) == 0 {
81            return x;
82        }
83        if ix == 0x7ff0000000000000 {
84            return x;
85        }
86        if ix > 0x7ff0000000000000 {
87            return __math_invalid(x);
88        }
89        // x is subnormal, normalize it
90        let ix2 = asuint64(x * p2d(52));
91        top = ix2 >> 52;
92        top -= 52;
93        // use normalized ix
94        return sqrt_inner(ix2, top);
95    }
96    sqrt_inner(ix, top)
97}
98
99#[inline]
100fn sqrt_inner(ix: u64, top: u64) -> f64 {
101    let mut top = top;
102    let mut m;
103
104    let even = top & 1;
105    m = (ix << 11) | 0x8000000000000000u64;
106    if even != 0 {
107        m >>= 1;
108    }
109    top = (top + 0x3ff) >> 1;
110
111    let three = 0xc0000000u64;
112
113    let i = ((ix >> 46) % 128) as usize;
114    let mut r = (RSQRT_TAB[i] as u64) << 16;
115    // |r sqrt(m) - 1| < 0x1.fdp-9
116    let mut s = mul32((m >> 32) as u32, r as u32) as u64;
117    // |s/sqrt(m) - 1| < 0x1.fdp-9
118    let mut d = mul32(s as u32, r as u32) as u64;
119    let mut u = three.wrapping_sub(d);
120    r = (mul32(r as u32, u as u32) as u64) << 1;
121    // |r sqrt(m) - 1| < 0x1.7bp-16
122    s = (mul32(s as u32, u as u32) as u64) << 1;
123    // |s/sqrt(m) - 1| < 0x1.7bp-16
124    d = mul32(s as u32, r as u32) as u64;
125    u = three.wrapping_sub(d);
126    r = (mul32(r as u32, u as u32) as u64) << 1;
127    // |r sqrt(m) - 1| < 0x1.3704p-29
128    r = r << 32;
129    s = mul64(m, r);
130    d = mul64(s, r);
131    u = (three << 32).wrapping_sub(d);
132    s = mul64(s, u);
133    // -0x1p-57 < s - sqrt(m) < 0x1.8001p-61
134    s = (s.wrapping_sub(2)) >> 9;
135
136    let d0 = m.wrapping_shl(42).wrapping_sub(s.wrapping_mul(s));
137    let d1 = s.wrapping_sub(d0);
138    let d2 = d1.wrapping_add(s).wrapping_add(1);
139    let s = s.wrapping_add((d1 >> 63) & 1);
140    let mut s = s & 0x000fffffffffffff;
141    s |= top << 52;
142    let mut y = asdouble(s);
143    // handle rounding modes and inexact exception
144    let tiny = if predict_false(d2 == 0) { 0u64 } else { 0x0010000000000000u64 };
145    let tiny = tiny | ((d1 ^ d2) & 0x8000000000000000);
146    let t = asdouble(tiny);
147    y = eval_as_double(y + t);
148    y
149}
150
151// ============================================================
152// sqrtf
153// ============================================================
154
155#[no_mangle]
156pub extern "C" fn sqrtf(x: f32) -> f32 {
157    let ix = asuint(x);
158    if predict_false(ix.wrapping_sub(0x00800000) >= 0x7f800000 - 0x00800000) {
159        // x < 0x1p-126 or inf or nan
160        if ix.wrapping_mul(2) == 0 {
161            return x;
162        }
163        if ix == 0x7f800000 {
164            return x;
165        }
166        if ix > 0x7f800000 {
167            return __math_invalidf(x);
168        }
169        // x is subnormal, normalize it
170        let ix2 = asuint(x * p2f(23));
171        return sqrtf_inner(ix2.wrapping_sub(23 << 23));
172    }
173    sqrtf_inner(ix)
174}
175
176#[inline]
177fn sqrtf_inner(ix: u32) -> f32 {
178    let even = ix & 0x00800000;
179    let m1 = (ix << 8) | 0x80000000u32;
180    let m0 = (ix << 7) & 0x7fffffff;
181    let m = if even != 0 { m0 } else { m1 };
182
183    // 2^e is the exponent part of the return value
184    let mut ey = ix >> 1;
185    ey = ey.wrapping_add(0x3f800000 >> 1);
186    ey &= 0x7f800000;
187
188    let three = 0xc0000000u32;
189    let i = ((ix >> 17) % 128) as usize;
190    let mut r = (RSQRT_TAB[i] as u32) << 16;
191    let mut s = mul32(m, r);
192    let mut d = mul32(s, r);
193    let mut u = three.wrapping_sub(d);
194    r = mul32(r, u) << 1;
195    s = mul32(s, u) << 1;
196    d = mul32(s, r);
197    u = three.wrapping_sub(d);
198    s = mul32(s, u);
199    // -0x1.03p-28 < s/sqrt(m) - 1 < 0x1.fp-31
200    s = (s.wrapping_sub(1)) >> 6;
201
202    let d0 = m.wrapping_shl(16).wrapping_sub(s.wrapping_mul(s));
203    let d1 = s.wrapping_sub(d0);
204    let d2 = d1.wrapping_add(s).wrapping_add(1);
205    let s = s.wrapping_add((d1 >> 31) & 1);
206    let mut s = s & 0x007fffff;
207    s |= ey;
208    let mut y = asfloat(s);
209    // handle rounding and inexact exception
210    let tiny = if predict_false(d2 == 0) { 0u32 } else { 0x01000000u32 };
211    let tiny = tiny | ((d1 ^ d2) & 0x80000000);
212    let t = asfloat(tiny);
213    y = eval_as_float(y + t);
214    y
215}
216
217// ============================================================
218// fmod
219// ============================================================
220
221#[no_mangle]
222pub extern "C" fn fmod(x: f64, y: f64) -> f64 {
223    let uxi = asuint64(x);
224    let uyi = asuint64(y);
225    let ex = ((uxi >> 52) & 0x7ff) as i32;
226    let ey = ((uyi >> 52) & 0x7ff) as i32;
227    let sx = uxi >> 63;
228
229    if uyi << 1 == 0 || is_nan(y) || ex == 0x7ff {
230        return (x * y) / (x * y);
231    }
232    if uxi << 1 <= uyi << 1 {
233        if uxi << 1 == uyi << 1 {
234            return 0.0 * x;
235        }
236        return x;
237    }
238
239    // normalize x and y
240    let mut uxi = uxi;
241    let mut uyi = uyi;
242    let mut ex = ex;
243    let mut ey = ey;
244
245    if ex == 0 {
246        let mut i = uxi << 12;
247        while (i as i64) >= 0 {
248            ex -= 1;
249            i <<= 1;
250        }
251        uxi <<= (-ex + 1) as u64;
252    } else {
253        uxi &= (-1i64 as u64) >> 12;
254        uxi |= 1u64 << 52;
255    }
256    if ey == 0 {
257        let mut i = uyi << 12;
258        while (i as i64) >= 0 {
259            ey -= 1;
260            i <<= 1;
261        }
262        uyi <<= (-ey + 1) as u64;
263    } else {
264        uyi &= (-1i64 as u64) >> 12;
265        uyi |= 1u64 << 52;
266    }
267
268    // x mod y
269    while ex > ey {
270        let i = uxi.wrapping_sub(uyi);
271        if (i as i64) >= 0 {
272            if i == 0 {
273                return 0.0 * x;
274            }
275            uxi = i;
276        }
277        uxi <<= 1;
278        ex -= 1;
279    }
280    let i = uxi.wrapping_sub(uyi);
281    if (i as i64) >= 0 {
282        if i == 0 {
283            return 0.0 * x;
284        }
285        uxi = i;
286    }
287    while uxi >> 52 == 0 {
288        uxi <<= 1;
289        ex -= 1;
290    }
291
292    // scale result
293    if ex > 0 {
294        uxi -= 1u64 << 52;
295        uxi |= (ex as u64) << 52;
296    } else {
297        uxi >>= (-ex + 1) as u64;
298    }
299    uxi |= sx << 63;
300    asdouble(uxi)
301}
302
303// ============================================================
304// fmodf
305// ============================================================
306
307#[no_mangle]
308pub extern "C" fn fmodf(x: f32, y: f32) -> f32 {
309    let uxi = asuint(x);
310    let uyi = asuint(y);
311    let ex = ((uxi >> 23) & 0xff) as i32;
312    let ey = ((uyi >> 23) & 0xff) as i32;
313    let sx = uxi & 0x80000000;
314
315    if uyi << 1 == 0 || is_nanf(y) || ex == 0xff {
316        return (x * y) / (x * y);
317    }
318    if uxi << 1 <= uyi << 1 {
319        if uxi << 1 == uyi << 1 {
320            return 0.0f32 * x;
321        }
322        return x;
323    }
324
325    // normalize x and y
326    let mut uxi = uxi;
327    let mut uyi = uyi;
328    let mut ex = ex;
329    let mut ey = ey;
330
331    if ex == 0 {
332        let mut i = uxi << 9;
333        while (i as i32) >= 0 {
334            ex -= 1;
335            i <<= 1;
336        }
337        uxi <<= (-ex + 1) as u32;
338    } else {
339        uxi &= (-1i32 as u32) >> 9;
340        uxi |= 1u32 << 23;
341    }
342    if ey == 0 {
343        let mut i = uyi << 9;
344        while (i as i32) >= 0 {
345            ey -= 1;
346            i <<= 1;
347        }
348        uyi <<= (-ey + 1) as u32;
349    } else {
350        uyi &= (-1i32 as u32) >> 9;
351        uyi |= 1u32 << 23;
352    }
353
354    // x mod y
355    while ex > ey {
356        let i = uxi.wrapping_sub(uyi);
357        if (i as i32) >= 0 {
358            if i == 0 {
359                return 0.0f32 * x;
360            }
361            uxi = i;
362        }
363        uxi <<= 1;
364        ex -= 1;
365    }
366    let i = uxi.wrapping_sub(uyi);
367    if (i as i32) >= 0 {
368        if i == 0 {
369            return 0.0f32 * x;
370        }
371        uxi = i;
372    }
373    while uxi >> 23 == 0 {
374        uxi <<= 1;
375        ex -= 1;
376    }
377
378    // scale result up
379    if ex > 0 {
380        uxi -= 1u32 << 23;
381        uxi |= (ex as u32) << 23;
382    } else {
383        uxi >>= (-ex + 1) as u32;
384    }
385    uxi |= sx;
386    asfloat(uxi)
387}