Skip to main content

c/
math_sqrtfmod.rs

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