| /* SPDX-License-Identifier: MIT */ |
| /* origin: core-math/src/binary64/hypot/hypot.c |
| * Copyright (c) 2022 Alexei Sibidanov. |
| * Ported to Rust in 2025, TG |
| * Approximate CORE-MATH commit: 8ea8ea35c518 |
| */ |
| |
| //! Euclidian distance via the pythagorean theorem (`√(x2 + y2)`). |
| //! |
| //! Per IEEE 754-2019: |
| //! |
| //! - Domain: `[−∞, +∞] × [−∞, +∞]` |
| //! - `hypot(±0, ±0)` is +0 |
| //! - `hypot(±∞, qNaN)` is +∞ |
| //! - `hypot(qNaN, ±∞)` is +∞. |
| //! - May raise overflow or underflow |
| |
| use super::sqrt; |
| #[allow(unused_imports)] // msrv compat |
| use super::support::Float; |
| use super::support::cold_path; |
| |
| #[cfg_attr(assert_no_panic, no_panic::no_panic)] |
| pub fn hypot(x: f64, y: f64) -> f64 { |
| return cr_hypot(x, y); |
| } |
| |
| fn cr_hypot(mut x: f64, mut y: f64) -> f64 { |
| let flag = get_flags(); |
| |
| let xi = x.to_bits(); |
| let yi = y.to_bits(); |
| |
| let emsk: u64 = 0x7ffu64 << 52; |
| let mut ex: u64 = xi & emsk; |
| let mut ey: u64 = yi & emsk; |
| /* emsk corresponds to the upper bits of NaN and Inf (apart the sign bit) */ |
| x = x.abs(); |
| y = y.abs(); |
| if ex == emsk || ey == emsk { |
| cold_path(); |
| |
| /* Either x or y is NaN or Inf */ |
| let wx: u64 = xi << 1; |
| let wy: u64 = yi << 1; |
| let wm: u64 = emsk << 1; |
| |
| let one_inf = (wx == wm) ^ (wy == wm); |
| let one_nan = x.is_nan() ^ y.is_nan(); |
| |
| // let nqnn: i32 = (((wx >> 52) == 0xfff) ^ ((wy >> 52) == 0xfff)) as i32; |
| // /* ninf is 1 when only one of x and y is +/-Inf |
| // nqnn is 1 when only one of x and y is qNaN |
| // IEEE 754 says that hypot(+/-Inf,qNaN)=hypot(qNaN,+/-Inf)=+Inf. */ |
| if one_inf && one_nan { |
| return f64::INFINITY; |
| } |
| return x + y; /* inf, sNaN */ |
| } |
| |
| let u: f64 = x.max(y); |
| let v: f64 = x.min(y); |
| let mut xd: u64 = u.to_bits(); |
| let mut yd: u64 = v.to_bits(); |
| ey = yd; |
| |
| if ey >> 52 == 0 { |
| cold_path(); |
| |
| if yd == 0 { |
| return f64::from_bits(xd); |
| } |
| |
| ex = xd; |
| |
| if ex >> 52 == 0 { |
| cold_path(); |
| |
| if ex == 0 { |
| return 0.0; |
| } |
| |
| return as_hypot_denorm(ex, ey); |
| } |
| |
| let nz: u32 = ey.leading_zeros(); |
| ey <<= nz - 11; |
| ey &= u64::MAX >> 12; |
| ey = ey.wrapping_sub(((nz as i64 - 12i64) << 52) as u64); |
| let t = ey; // why did they do this? |
| yd = t; |
| } |
| |
| let de: u64 = xd.wrapping_sub(yd); |
| if de > (27_u64 << 52) { |
| cold_path(); |
| return hf64!("0x1p-27").fma(v, u); |
| } |
| |
| let off: i64 = (0x3ff_i64 << 52) - (xd & emsk) as i64; |
| xd = xd.wrapping_add(off as u64); |
| yd = yd.wrapping_add(off as u64); |
| x = f64::from_bits(xd); |
| y = f64::from_bits(yd); |
| let x2: f64 = x * x; |
| let dx2: f64 = x.fma(x, -x2); |
| let y2: f64 = y * y; |
| let dy2: f64 = y.fma(y, -y2); |
| let r2: f64 = x2 + y2; |
| let ir2: f64 = 0.5 / r2; |
| let dr2: f64 = ((x2 - r2) + y2) + (dx2 + dy2); |
| let mut th: f64 = sqrt(r2); |
| let rsqrt: f64 = th * ir2; |
| let dz: f64 = dr2 - th.fma(th, -r2); |
| let mut tl: f64 = rsqrt * dz; |
| th = fasttwosum(th, tl, &mut tl); |
| let mut thd: u64 = th.to_bits(); |
| let tld = tl.abs().to_bits(); |
| ex = thd; |
| ey = tld; |
| ex &= 0x7ff_u64 << 52; |
| let aidr: u64 = ey.wrapping_add(0x3fe_u64 << 52).wrapping_sub(ex); |
| let mid: u64 = (aidr.wrapping_sub(0x3c90000000000000).wrapping_add(16)) >> 5; |
| if mid == 0 || !(0x39b0000000000000_u64..=0x3c9fffffffffff80_u64).contains(&aidr) { |
| cold_path(); |
| thd = as_hypot_hard(x, y, flag).to_bits(); |
| } |
| thd = thd.wrapping_sub(off as u64); |
| if thd >= (0x7ff_u64 << 52) { |
| cold_path(); |
| return as_hypot_overflow(); |
| } |
| |
| f64::from_bits(thd) |
| } |
| |
| fn fasttwosum(x: f64, y: f64, e: &mut f64) -> f64 { |
| let s: f64 = x + y; |
| let z: f64 = s - x; |
| *e = y - z; |
| s |
| } |
| |
| fn as_hypot_overflow() -> f64 { |
| let z: f64 = hf64!("0x1.fffffffffffffp1023"); |
| let f = z + z; |
| if f > z { |
| // errno = ERANGE |
| } |
| f |
| } |
| |
| /// Here the square root is refined by Newton iterations: x^2+y^2 is exact |
| /// and fits in a 128-bit integer, so the approximation is squared (which |
| /// also fits in a 128-bit integer), compared and adjusted if necessary using |
| /// the exact value of x^2+y^2. |
| fn as_hypot_hard(x: f64, y: f64, flag: FExcept) -> f64 { |
| let op: f64 = 1.0 + hf64!("0x1p-54"); |
| let om: f64 = 1.0 - hf64!("0x1p-54"); |
| let mut xi: u64 = x.to_bits(); |
| let yi: u64 = y.to_bits(); |
| let mut bm: u64 = (xi & (u64::MAX >> 12)) | 1u64 << 52; |
| let mut lm: u64 = (yi & (u64::MAX >> 12)) | 1u64 << 52; |
| let be: i32 = (xi >> 52) as i32; |
| let le: i32 = (yi >> 52) as i32; |
| let ri: u64 = sqrt(x * x + y * y).to_bits(); |
| let bs: i32 = 2; |
| let mut rm: u64 = ri & (u64::MAX >> 12); |
| let mut re: i32 = (ri >> 52) as i32 - 0x3ff; |
| rm |= 1u64 << 52; |
| |
| for _ in 0..3 { |
| if rm == 1u64 << 52 { |
| rm = u64::MAX >> 11; |
| re -= 1; |
| } else { |
| cold_path(); |
| rm -= 1; |
| } |
| } |
| |
| bm <<= bs; |
| let mut m2: u64 = bm.wrapping_mul(bm); |
| let de: i32 = be - le; |
| let mut ls: i32 = bs - de; |
| |
| if ls >= 0 { |
| lm <<= ls; |
| m2 = m2.wrapping_add(lm.wrapping_mul(lm)); |
| } else { |
| cold_path(); |
| let lm2: u128 = (lm as u128) * (lm as u128); |
| ls *= 2; |
| m2 = m2.wrapping_add((lm2 >> -ls) as u64); |
| m2 |= ((lm2 << (128 + ls)) != 0) as u64; |
| } |
| |
| let k: i32 = bs + re; |
| let mut d: i64; |
| |
| loop { |
| rm += 1 + (rm >= (1u64 << 53)) as u64; |
| let tm: u64 = rm << k; |
| let rm2: u64 = tm.wrapping_mul(tm); |
| d = m2 as i64 - rm2 as i64; |
| |
| if d <= 0 { |
| break; |
| } |
| } |
| |
| if d == 0 { |
| set_flags(flag); |
| } else if op == om { |
| let tm: u64 = (rm << k) - (1 << (k - (rm <= (1u64 << 53)) as i32)); |
| d = m2 as i64 - (tm.wrapping_mul(tm)) as i64; |
| |
| if d == 0 { |
| cold_path(); |
| rm -= rm & 1; |
| } else { |
| rm = rm.wrapping_add((d >> 63) as u64); |
| } |
| } else { |
| cold_path(); |
| rm -= ((op == 1.0) as u64) << (rm > (1u64 << 53)) as u32; |
| } |
| |
| if rm >= (1u64 << 53) { |
| rm >>= 1; |
| re += 1; |
| } |
| |
| let e: u64 = (be - 1 + re) as u64; |
| xi = (e << 52) + rm; |
| |
| f64::from_bits(xi) |
| } |
| |
| fn as_hypot_denorm(mut a: u64, mut b: u64) -> f64 { |
| let op: f64 = 1.0 + hf64!("0x1p-54"); |
| let om: f64 = 1.0 - hf64!("0x1p-54"); |
| let af: f64 = a as i64 as f64; |
| let bf: f64 = b as i64 as f64; |
| a <<= 1; |
| b <<= 1; |
| // Is this casting right? |
| let mut rm: u64 = sqrt(af * af + bf * bf) as u64; |
| let tm: u64 = rm << 1; |
| let mut d: i64 = (a.wrapping_mul(a) as i64) |
| .wrapping_sub(tm.wrapping_mul(tm) as i64) |
| .wrapping_add(b.wrapping_mul(b) as i64); |
| let sd: i64 = d >> 63; |
| let um: i64 = ((rm as i64) ^ sd) - sd; |
| let mut drm: i64 = sd + 1; |
| let mut dd: i64 = (um << 3) + 4; |
| let mut p_d: i64; |
| rm -= drm as u64; |
| drm += sd; |
| loop { |
| p_d = d; |
| rm = rm.wrapping_add(drm as u64); |
| d = d.wrapping_sub(dd); |
| dd = dd.wrapping_add(8); |
| if (d ^ p_d) <= 0 { |
| cold_path(); |
| break; |
| } |
| } |
| p_d = (sd & d) + (!sd & p_d); |
| if p_d != 0 { |
| if op == om { |
| let sum: i64 = p_d |
| .wrapping_sub(4u64.wrapping_mul(rm) as i64) |
| .wrapping_sub(1); |
| |
| if sum != 0 { |
| rm = rm.wrapping_add((sum >> 63).wrapping_add(1) as u64); |
| } else { |
| cold_path(); |
| rm += rm & 1; |
| } |
| } else { |
| cold_path(); |
| rm += (op > 1.0) as u64; |
| } |
| } else { |
| cold_path(); |
| } |
| |
| let xi: u64 = rm; |
| f64::from_bits(xi) |
| } |
| |
| type FExcept = u32; |
| |
| fn set_flags(_flag: FExcept) {} |
| |
| fn get_flags() -> FExcept { |
| 0 |
| } |