macro_rules! unary {
($($(#[$m:meta])* $name:ident => $f:ident;)*) => {$(
$(#[$m])*
#[inline]
#[must_use]
pub fn $name(x: f64) -> f64 {
libm::$f(x)
}
)*};
}
unary! {
sin => sin;
cos => cos;
tan => tan;
asin => asin;
acos => acos;
atan => atan;
sinh => sinh;
cosh => cosh;
tanh => tanh;
asinh => asinh;
acosh => acosh;
atanh => atanh;
exp => exp;
exp2 => exp2;
exp_m1 => expm1;
ln => log;
ln_1p => log1p;
log2 => log2;
log10 => log10;
cbrt => cbrt;
}
#[inline]
#[must_use]
pub fn sin_cos(x: f64) -> (f64, f64) {
libm::sincos(x)
}
#[inline]
#[must_use]
pub fn atan2(y: f64, x: f64) -> f64 {
libm::atan2(y, x)
}
#[inline]
#[must_use]
pub fn hypot(x: f64, y: f64) -> f64 {
libm::hypot(x, y)
}
#[inline]
#[must_use]
pub fn powf(x: f64, y: f64) -> f64 {
libm::pow(x, y)
}
#[inline]
#[must_use]
pub fn log(x: f64, base: f64) -> f64 {
libm::log(x) / libm::log(base)
}
#[inline]
#[must_use]
pub fn next_down(x: f64) -> f64 {
if x == 0.0 {
return -f64::from_bits(1);
}
let bits = x.to_bits();
f64::from_bits(if x > 0.0 { bits - 1 } else { bits + 1 })
}
#[inline]
#[must_use]
pub fn powi(x: f64, n: i32) -> f64 {
let mut base = x;
let mut rest = n.unsigned_abs();
let mut acc = 1.0;
loop {
if rest & 1 == 1 {
acc *= base;
}
rest >>= 1;
if rest == 0 {
break;
}
base *= base;
}
if n < 0 { 1.0 / acc } else { acc }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn atan2_of_the_gear_arc_has_the_same_bits_everywhere() {
let v = atan2(1.9422128270990565e-6, 1.271166962267458e-5);
assert_eq!(v, 1.5161718682801548e-1);
}
#[test]
fn powi_squares_exactly_and_inverts_negative_powers() {
let x = 1.0000000000000002;
assert_eq!(powi(x, 0), 1.0);
assert_eq!(powi(x, 1), x);
assert_eq!(powi(x, 2).to_bits(), (x * x).to_bits());
assert_eq!(powi(x, 3).to_bits(), (x * (x * x)).to_bits());
assert_eq!(powi(2.0, -3), 0.125);
assert_eq!(powi(0.0, 0), 1.0);
assert_eq!(powi(-2.0, i32::MIN + 1), 0.0);
}
}