| /* 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 | |
| } |