#[cfg(feature = "std")]
#[inline]
#[must_use]
pub fn expm1(x: f64) -> f64 {
x.exp_m1()
}
#[cfg(all(not(feature = "std"), feature = "libm"))]
#[inline]
#[must_use]
pub fn expm1(x: f64) -> f64 {
libm::expm1(x)
}
#[cfg(feature = "std")]
#[inline]
#[must_use]
pub fn log1p(x: f64) -> f64 {
x.ln_1p()
}
#[cfg(all(not(feature = "std"), feature = "libm"))]
#[inline]
#[must_use]
pub fn log1p(x: f64) -> f64 {
libm::log1p(x)
}
#[must_use]
pub fn fsum(xs: &[f64]) -> f64 {
let mut sum = 0.0;
let mut correction = 0.0;
for &x in xs {
let t = sum + x;
if sum.abs() >= x.abs() {
correction += (sum - t) + x;
} else {
correction += (x - t) + sum;
}
sum = t;
}
sum + correction
}
#[inline]
#[must_use]
#[allow(clippy::suboptimal_flops)] pub fn isclose(a: f64, b: f64, rel_tol: f64, abs_tol: f64) -> bool {
(a - b).abs() <= rel_tol * b.abs().max(a.abs()) + abs_tol
}
#[must_use]
pub fn frexp(x: f64) -> (f64, i32) {
if x == 0.0 || x.is_nan() || x.is_infinite() {
return (x, 0);
}
let bits = x.to_bits();
let exponent = ((bits >> 52) & 0x7FF) as i32;
if exponent == 0 {
let (m, e) = frexp(x * 4503599627370496.0); return (m, e - 52);
}
let mantissa = (bits & !(0x7FFu64 << 52)) | (1022u64 << 52);
(f64::from_bits(mantissa), exponent - 1022)
}
#[must_use]
pub fn ldexp(x: f64, e: i32) -> f64 {
if x == 0.0 || x.is_nan() || x.is_infinite() {
return x;
}
let mut bits = x.to_bits();
let exponent = ((bits >> 52) & 0x7FF) as i32;
if exponent == 0 {
return ldexp(x * 4503599627370496.0, e - 52); }
let new_exp = exponent + e;
if new_exp <= 0 {
let mut r = x;
let mut k = e;
while k < 0 {
r *= 0.5;
k += 1;
}
return r;
}
if new_exp >= 0x7FF {
return if bits >> 63 == 1 { f64::NEG_INFINITY } else { f64::INFINITY };
}
bits = (bits & !(0x7FFu64 << 52)) | ((new_exp as u64) << 52);
f64::from_bits(bits)
}
#[must_use]
pub fn modf(x: f64) -> (f64, f64) {
if x.is_nan() || x.is_infinite() {
return (x, 0.0);
}
let integral = trunc_f64(x);
(x - integral, integral)
}
#[inline]
#[must_use]
fn trunc_f64(x: f64) -> f64 {
if x == 0.0 || x.is_nan() || x.is_infinite() {
return x;
}
let bits = x.to_bits();
let exponent = ((bits >> 52) & 0x7FF) as i32;
if exponent == 0 {
return f64::from_bits(bits & (1u64 << 63)); }
let unbiased = exponent - 1023;
if unbiased >= 52 {
return x;
}
if unbiased < 0 {
return f64::from_bits(bits & (1u64 << 63)); }
let keep = 52 - unbiased; let mask = !((1u64 << keep) - 1);
f64::from_bits(bits & mask)
}
#[must_use]
pub fn nextafter(x: f64, y: f64) -> f64 {
if x.is_nan() || y.is_nan() {
return f64::NAN;
}
if x == y {
return y;
}
if x == 0.0 {
let mut r = f64::from_bits(1);
if y < 0.0 {
r = -r;
}
return r;
}
let mut bits = x.to_bits();
if (x < y) == (x > 0.0) {
bits += 1;
} else {
bits -= 1;
}
f64::from_bits(bits)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn expm1_log1p_small() {
assert!((expm1(1e-10) - 1e-10).abs() < 1e-20);
assert!((log1p(1e-10) - 1e-10).abs() < 1e-20);
assert!((expm1(0.0) - 0.0).abs() < 1e-20);
assert!((log1p(0.0) - 0.0).abs() < 1e-20);
}
#[test]
fn fsum_exactness() {
assert_eq!(fsum(&[1e16, 1.0, -1e16]), 1.0);
assert_eq!(fsum(&[0.1; 10]), 1.0);
}
#[test]
fn isclose_behavior() {
assert!(isclose(0.1 + 0.2, 0.3, 1e-9, 1e-12));
assert!(!isclose(1.0, 2.0, 1e-9, 1e-12));
assert!(!isclose(f64::NAN, f64::NAN, 1.0, 1.0));
assert!(isclose(1e-300, 1e-300, 1e-9, 1e-320));
}
#[test]
fn frexp_ldexp_roundtrip() {
for &x in &[0.5, 1.0, 2.0, 6.0, 123.456, 1e-300, -7.5] {
let (m, e) = frexp(x);
assert!(m.abs() >= 0.5 && m.abs() < 1.0 || x == 0.0);
assert!((ldexp(m, e) - x).abs() < x.abs() * 1e-12 || x == 0.0);
}
assert_eq!(frexp(0.0), (0.0, 0));
assert_eq!(frexp(0.75), (0.75, 0));
assert_eq!(frexp(6.0), (0.75, 3));
assert!(ldexp(1.0, 2000).is_infinite());
assert_eq!(ldexp(1.0, -2000), 0.0);
}
#[test]
fn modf_split() {
assert_eq!(modf(2.7).1, 2.0);
assert_eq!(modf(-2.7).1, -2.0);
assert_eq!(modf(0.0).1, 0.0);
assert_eq!(modf(f64::INFINITY).1, 0.0);
}
#[test]
fn nextafter_stepping() {
assert!(nextafter(1.0, 2.0) > 1.0);
assert!(nextafter(1.0, 0.0) < 1.0);
assert_eq!(nextafter(0.0, 1.0), f64::from_bits(1));
assert_eq!(nextafter(1.0, 1.0), 1.0);
assert!(nextafter(f64::MAX, f64::INFINITY).is_infinite());
}
}