#[cfg(all(feature = "libm", not(feature = "std"), not(test)))]
use crate::math::Float as _;
#[inline]
pub(crate) fn two_sum(u: f64, v: f64) -> (f64, f64) {
let s = u + v;
let up = s - v;
let vpp = s - up;
(s, -((up - u) + (vpp - v)))
}
pub(crate) fn ang_round(x: f64) -> f64 {
const Z: f64 = 1.0 / 16.0;
let mut y = x.abs();
if y < Z {
y = Z - (Z - y);
}
if x == 0.0 {
0.0
} else {
y.copysign(x)
}
}
pub(crate) fn ang_normalize(x: f64) -> f64 {
if x > -180.0 && x <= 180.0 {
return x; }
let z = if x.is_finite() { x % 360.0 } else { f64::NAN };
let z = if x == 0.0 { x } else { z };
let y = if z < -180.0 {
z + 360.0
} else if z < 180.0 {
z
} else {
z - 360.0
};
if y == -180.0 {
180.0
} else {
y
}
}
pub(crate) fn lat_fix(x: f64) -> f64 {
if x.abs() > 90.0 {
f64::NAN
} else {
x
}
}
pub(crate) fn ang_diff(x: f64, y: f64) -> (f64, f64) {
let (d, t) = two_sum(ang_normalize(-x), ang_normalize(y));
let d = ang_normalize(d);
if d == 180.0 && t > 0.0 {
two_sum(-180.0, t)
} else {
two_sum(d, t)
}
}
pub(crate) fn sincosd(x: f64) -> (f64, f64) {
let r = if x.abs() < 720.0 { x } else { x % 360.0 };
let mut q = (r / 90.0) as i64;
let mut r = r - 90.0 * q as f64;
if r > 45.0 {
q += 1;
r -= 90.0;
} else if r < -45.0 {
q -= 1;
r += 90.0;
}
let (s, c) = if 2.0 * r.abs() == 90.0 {
(
core::f64::consts::FRAC_1_SQRT_2.copysign(r),
core::f64::consts::FRAC_1_SQRT_2,
)
} else if 3.0 * r.abs() == 90.0 {
(0.5f64.copysign(r), 3f64.sqrt() / 2.0)
} else {
r.to_radians().sin_cos()
};
let (s, c) = match q & 3 {
0 => (s, c),
1 => (c, -s),
2 => (-s, -c),
_ => (-c, s),
};
(if s == 0.0 { s.copysign(x) } else { s }, c + 0.0)
}
pub(crate) fn atan2d(mut y: f64, mut x: f64) -> f64 {
let mut q = if y.abs() > x.abs() {
core::mem::swap(&mut x, &mut y);
2
} else {
0
};
if x < 0.0 {
q += 1;
x = -x;
}
let ang = y.atan2(x).to_degrees();
match q {
1 => (if y >= 0.0 { 180.0 } else { -180.0 }) - ang,
2 => 90.0 - ang,
3 => -90.0 + ang,
_ => ang,
}
}
pub(crate) fn tand(x: f64) -> f64 {
const OVERFLOW: f64 = 1.0 / (f64::EPSILON * f64::EPSILON);
let (s, c) = sincosd(x);
(s / c).clamp(-OVERFLOW, OVERFLOW)
}
pub(crate) fn atand(x: f64) -> f64 {
atan2d(x, 1.0)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn exact_where_they_should_be() {
for (deg, s, c) in [
(0.0, 0.0, 1.0),
(90.0, 1.0, 0.0),
(180.0, 0.0, -1.0),
(-90.0, -1.0, 0.0),
(270.0, -1.0, 0.0),
(720.0, 0.0, 1.0),
] {
assert_eq!(sincosd(deg), (s, c), "{deg}");
}
let h = core::f64::consts::FRAC_1_SQRT_2;
assert_eq!(sincosd(45.0), (h, h));
assert_eq!(sincosd(-135.0), (-h, -h));
assert_eq!(sincosd(30.0), (0.5, 3f64.sqrt() / 2.0));
assert_eq!(sincosd(150.0).0, 0.5);
assert_eq!(atand(1.0), 45.0);
assert_eq!(atand(f64::INFINITY), 90.0);
assert_eq!(tand(45.0), 1.0);
assert_eq!(tand(0.0), 0.0);
assert!(tand(90.0) > 1e30 && tand(-90.0) < -1e30);
assert!(tand(f64::NAN).is_nan());
assert!(lat_fix(90.5).is_nan());
assert_eq!(ang_normalize(-180.0), 180.0);
let (d, e) = ang_diff(-179.0, 179.0);
assert_eq!(d + e, -2.0);
}
}