pub const MATH_E: f64 = std::f64::consts::E;
pub const MATH_LN2: f64 = std::f64::consts::LN_2;
pub const MATH_LN10: f64 = std::f64::consts::LN_10;
pub const MATH_LOG2E: f64 = std::f64::consts::LOG2_E;
pub const MATH_LOG10E: f64 = std::f64::consts::LOG10_E;
pub const MATH_PI: f64 = std::f64::consts::PI;
pub const MATH_SQRT1_2: f64 = std::f64::consts::FRAC_1_SQRT_2;
pub const MATH_SQRT2: f64 = std::f64::consts::SQRT_2;
pub fn math_abs(x: f64) -> f64 {
x.abs()
}
pub fn math_ceil(x: f64) -> f64 {
x.ceil()
}
pub fn math_floor(x: f64) -> f64 {
x.floor()
}
pub fn math_round(x: f64) -> f64 {
let rounded = (x + 0.5).floor();
if rounded == 0.0 && x.is_sign_negative() {
-0.0_f64
} else {
rounded
}
}
pub fn math_trunc(x: f64) -> f64 {
x.trunc()
}
pub fn math_sign(x: f64) -> f64 {
if x.is_nan() {
f64::NAN
} else if x > 0.0 {
1.0
} else if x < 0.0 {
-1.0
} else {
x
}
}
pub fn math_max(values: &[f64]) -> f64 {
let mut result = f64::NEG_INFINITY;
for &v in values {
if v.is_nan() {
return f64::NAN;
}
if v > result || (v == 0.0 && result == 0.0 && result.is_sign_negative()) {
result = v;
}
}
result
}
pub fn math_min(values: &[f64]) -> f64 {
let mut result = f64::INFINITY;
for &v in values {
if v.is_nan() {
return f64::NAN;
}
if v < result || (v == 0.0 && result == 0.0 && v.is_sign_negative()) {
result = v;
}
}
result
}
pub fn math_pow(base: f64, exponent: f64) -> f64 {
if base.abs() == 1.0 && exponent.is_infinite() {
return f64::NAN;
}
base.powf(exponent)
}
pub fn math_sqrt(x: f64) -> f64 {
x.sqrt()
}
pub fn math_cbrt(x: f64) -> f64 {
x.cbrt()
}
pub fn math_hypot(values: &[f64]) -> f64 {
if values.is_empty() {
return 0.0;
}
let mut max = 0.0;
let mut saw_nan = false;
for &value in values {
let abs = value.abs();
if abs.is_infinite() {
return f64::INFINITY;
}
if abs.is_nan() {
saw_nan = true;
continue;
}
if abs > max {
max = abs;
}
}
if saw_nan {
return f64::NAN;
}
if max == 0.0 {
return 0.0;
}
let scaled_sum = values
.iter()
.map(|value| {
let scaled = value / max;
scaled * scaled
})
.sum::<f64>();
max * scaled_sum.sqrt()
}
pub fn math_log(x: f64) -> f64 {
x.ln()
}
pub fn math_log2(x: f64) -> f64 {
x.log2()
}
pub fn math_log10(x: f64) -> f64 {
x.log10()
}
pub fn math_sin(x: f64) -> f64 {
x.sin()
}
pub fn math_cos(x: f64) -> f64 {
x.cos()
}
pub fn math_tan(x: f64) -> f64 {
x.tan()
}
pub fn math_asin(x: f64) -> f64 {
x.asin()
}
pub fn math_acos(x: f64) -> f64 {
x.acos()
}
pub fn math_atan(x: f64) -> f64 {
x.atan()
}
pub fn math_atan2(y: f64, x: f64) -> f64 {
y.atan2(x)
}
pub fn math_random() -> f64 {
use std::cell::Cell;
#[cfg(miri)]
thread_local! {
static STATE: Cell<u64> = const { Cell::new(12_345_678_901_234_567_u64) };
}
#[cfg(not(miri))]
thread_local! {
static STATE: Cell<u64> = {
use std::time::{SystemTime, UNIX_EPOCH};
Cell::new(
SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| {
d.subsec_nanos() as u64
^ (d.as_secs().wrapping_mul(6_364_136_223_846_793_005))
})
.unwrap_or(12_345_678_901_234_567)
| 1, )
};
}
STATE.with(|s| {
let mut x = s.get();
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
s.set(x);
(x >> 11) as f64 / (1u64 << 53) as f64
})
}
pub fn math_clz32(x: f64) -> u32 {
to_uint32_number(x).leading_zeros()
}
pub fn math_imul(a: f64, b: f64) -> i32 {
to_uint32_number(a).wrapping_mul(to_uint32_number(b)) as i32
}
pub fn math_fround(x: f64) -> f64 {
(x as f32) as f64
}
fn to_uint32_number(n: f64) -> u32 {
if n.is_nan() || n.is_infinite() || n == 0.0 {
return 0;
}
n.trunc().rem_euclid(4_294_967_296.0) as u32
}
pub fn math_exp(x: f64) -> f64 {
x.exp()
}
pub fn math_expm1(x: f64) -> f64 {
x.exp_m1()
}
pub fn math_log1p(x: f64) -> f64 {
x.ln_1p()
}
pub fn math_sinh(x: f64) -> f64 {
x.sinh()
}
pub fn math_cosh(x: f64) -> f64 {
x.cosh()
}
pub fn math_tanh(x: f64) -> f64 {
x.tanh()
}
pub fn math_asinh(x: f64) -> f64 {
x.asinh()
}
pub fn math_acosh(x: f64) -> f64 {
x.acosh()
}
pub fn math_atanh(x: f64) -> f64 {
x.atanh()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_math_pi_approx() {
assert!((MATH_PI - 3.141_592_653_589_793).abs() < 1e-15);
}
#[test]
fn test_math_e_approx() {
assert!((MATH_E - 2.718_281_828_459_045).abs() < 1e-15);
}
#[test]
fn test_abs_positive() {
assert_eq!(math_abs(3.5), 3.5);
}
#[test]
fn test_abs_negative() {
assert_eq!(math_abs(-3.5), 3.5);
}
#[test]
fn test_abs_zero() {
assert_eq!(math_abs(0.0), 0.0);
assert_eq!(math_abs(-0.0), 0.0);
}
#[test]
fn test_abs_nan() {
assert!(math_abs(f64::NAN).is_nan());
}
#[test]
fn test_abs_infinity() {
assert_eq!(math_abs(f64::INFINITY), f64::INFINITY);
assert_eq!(math_abs(f64::NEG_INFINITY), f64::INFINITY);
}
#[test]
fn test_ceil_positive_fraction() {
assert_eq!(math_ceil(1.2), 2.0);
}
#[test]
fn test_ceil_negative_fraction() {
assert_eq!(math_ceil(-1.2), -1.0);
}
#[test]
fn test_ceil_whole() {
assert_eq!(math_ceil(3.0), 3.0);
}
#[test]
fn test_floor_positive_fraction() {
assert_eq!(math_floor(1.9), 1.0);
}
#[test]
fn test_floor_negative_fraction() {
assert_eq!(math_floor(-1.1), -2.0);
}
#[test]
fn test_floor_whole() {
assert_eq!(math_floor(2.0), 2.0);
}
#[test]
fn test_round_half_up() {
assert_eq!(math_round(1.5), 2.0);
assert_eq!(math_round(2.5), 3.0);
}
#[test]
fn test_round_negative_half() {
assert_eq!(math_round(-1.5), -1.0);
let r = math_round(-0.5);
assert_eq!(r, 0.0);
assert!(r.is_sign_negative());
}
#[test]
fn test_round_below_half() {
assert_eq!(math_round(1.4), 1.0);
assert_eq!(math_round(-1.4), -1.0);
}
#[test]
fn test_trunc_positive() {
assert_eq!(math_trunc(1.9), 1.0);
}
#[test]
fn test_trunc_negative() {
assert_eq!(math_trunc(-1.9), -1.0);
}
#[test]
fn test_trunc_zero() {
assert_eq!(math_trunc(0.5), 0.0);
assert_eq!(math_trunc(-0.5), -0.0);
}
#[test]
fn test_sign_positive() {
assert_eq!(math_sign(5.0), 1.0);
}
#[test]
fn test_sign_negative() {
assert_eq!(math_sign(-3.0), -1.0);
}
#[test]
fn test_sign_zero() {
assert_eq!(math_sign(0.0), 0.0);
assert!(math_sign(-0.0_f64).is_sign_negative());
}
#[test]
fn test_sign_nan() {
assert!(math_sign(f64::NAN).is_nan());
}
#[test]
fn test_max_basic() {
assert_eq!(math_max(&[1.0, 3.0, 2.0]), 3.0);
}
#[test]
fn test_max_empty() {
assert_eq!(math_max(&[]), f64::NEG_INFINITY);
}
#[test]
fn test_max_nan_propagates() {
assert!(math_max(&[1.0, f64::NAN, 3.0]).is_nan());
}
#[test]
fn test_min_basic() {
assert_eq!(math_min(&[1.0, 3.0, 2.0]), 1.0);
}
#[test]
fn test_min_empty() {
assert_eq!(math_min(&[]), f64::INFINITY);
}
#[test]
fn test_min_nan_propagates() {
assert!(math_min(&[1.0, f64::NAN, 3.0]).is_nan());
}
#[test]
fn test_pow_integer() {
assert!((math_pow(2.0, 10.0) - 1024.0).abs() < 1e-10);
}
#[test]
fn test_pow_fractional_exponent() {
assert!((math_pow(4.0, 0.5) - 2.0).abs() < 1e-15);
}
#[test]
fn test_pow_zero_exponent() {
assert_eq!(math_pow(5.0, 0.0), 1.0);
assert_eq!(math_pow(0.0, 0.0), 1.0);
}
#[test]
fn test_pow_nan_base() {
assert!(math_pow(f64::NAN, 2.0).is_nan());
}
#[test]
fn test_sqrt_perfect_square() {
assert_eq!(math_sqrt(4.0), 2.0);
assert_eq!(math_sqrt(9.0), 3.0);
}
#[test]
fn test_sqrt_negative() {
assert!(math_sqrt(-1.0).is_nan());
}
#[test]
fn test_sqrt_zero() {
assert_eq!(math_sqrt(0.0), 0.0);
}
#[test]
fn test_cbrt_positive() {
assert!((math_cbrt(27.0) - 3.0).abs() < 1e-14);
}
#[test]
fn test_cbrt_negative() {
assert!((math_cbrt(-8.0) - (-2.0)).abs() < 1e-14);
}
#[test]
fn test_hypot_3_4_5() {
assert!((math_hypot(&[3.0, 4.0]) - 5.0).abs() < 1e-14);
}
#[test]
fn test_hypot_empty() {
assert_eq!(math_hypot(&[]), 0.0);
}
#[test]
fn test_hypot_infinity() {
assert_eq!(math_hypot(&[f64::INFINITY, 0.0]), f64::INFINITY);
}
#[test]
fn test_hypot_nan() {
assert!(math_hypot(&[f64::NAN, 1.0]).is_nan());
}
#[test]
fn test_log_e() {
assert!((math_log(std::f64::consts::E) - 1.0).abs() < 1e-15);
}
#[test]
fn test_log_zero() {
assert_eq!(math_log(0.0), f64::NEG_INFINITY);
}
#[test]
fn test_log_negative() {
assert!(math_log(-1.0).is_nan());
}
#[test]
fn test_log2_power_of_two() {
assert!((math_log2(8.0) - 3.0).abs() < 1e-10);
}
#[test]
fn test_log10_power_of_ten() {
assert!((math_log10(1000.0) - 3.0).abs() < 1e-14);
}
#[test]
fn test_sin_zero() {
assert!((math_sin(0.0)).abs() < 1e-15);
}
#[test]
fn test_sin_half_pi() {
assert!((math_sin(std::f64::consts::FRAC_PI_2) - 1.0).abs() < 1e-14);
}
#[test]
fn test_cos_zero() {
assert!((math_cos(0.0) - 1.0).abs() < 1e-15);
}
#[test]
fn test_cos_pi() {
assert!((math_cos(std::f64::consts::PI) + 1.0).abs() < 1e-14);
}
#[test]
fn test_tan_zero() {
assert!(math_tan(0.0).abs() < 1e-15);
}
#[test]
fn test_random_in_range() {
for _ in 0..100 {
let v = math_random();
assert!((0.0..1.0).contains(&v), "random value {v} not in [0, 1)");
}
}
#[test]
fn test_random_produces_different_values() {
let a = math_random();
let b = math_random();
assert_ne!(a, b);
}
#[test]
fn test_clz32_one() {
assert_eq!(math_clz32(1.0), 31);
}
#[test]
fn test_clz32_zero() {
assert_eq!(math_clz32(0.0), 32);
}
#[test]
fn test_clz32_large() {
assert_eq!(math_clz32(1000.0), 22);
}
#[test]
fn test_imul_basic() {
assert_eq!(math_imul(3.0, 4.0), 12);
}
#[test]
fn test_imul_negative() {
assert_eq!(math_imul(-5.0, 3.0), -15);
}
#[test]
fn test_imul_overflow_wraps() {
assert_eq!(math_imul(0xffff_ffff_u32 as f64, 5.0), -5);
}
#[test]
fn test_fround_identity_for_f32_exact() {
assert_eq!(math_fround(1.0), 1.0);
}
#[test]
fn test_fround_reduces_precision() {
let exact = 1.337_f32 as f64;
assert_eq!(math_fround(1.337), exact);
}
#[test]
fn test_fround_nan() {
assert!(math_fround(f64::NAN).is_nan());
}
#[test]
fn test_ieee754_abs_negative_zero() {
assert!(math_abs(-0.0_f64).is_sign_positive());
}
#[test]
fn test_ieee754_sqrt_infinity() {
assert_eq!(math_sqrt(f64::INFINITY), f64::INFINITY);
}
#[test]
fn test_ieee754_pow_neg1_infinity() {
assert!(math_pow(-1.0, f64::INFINITY).is_nan());
assert!(math_pow(-1.0, f64::NEG_INFINITY).is_nan());
assert!(math_pow(1.0, f64::INFINITY).is_nan());
assert!(math_pow(1.0, f64::NEG_INFINITY).is_nan());
}
#[test]
fn test_ieee754_log_one() {
assert_eq!(math_log(1.0), 0.0);
}
#[test]
fn test_ieee754_hypot_inf_over_nan() {
assert_eq!(math_hypot(&[f64::INFINITY, f64::NAN]), f64::INFINITY);
}
#[test]
fn test_round_negative_zero() {
let r = math_round(-0.0_f64);
assert_eq!(r, 0.0);
assert!(r.is_sign_negative());
}
#[test]
fn test_round_small_negative() {
let r = math_round(-0.3);
assert_eq!(r, 0.0);
assert!(r.is_sign_negative());
}
#[test]
fn test_max_negative_zero_positive_zero() {
let r = math_max(&[-0.0, 0.0]);
assert_eq!(r, 0.0);
assert!(r.is_sign_positive());
}
#[test]
fn test_min_positive_zero_negative_zero() {
let r = math_min(&[0.0, -0.0]);
assert_eq!(r, 0.0);
assert!(r.is_sign_negative());
}
#[test]
fn test_exp_zero() {
assert_eq!(math_exp(0.0), 1.0);
}
#[test]
fn test_exp_one() {
assert!((math_exp(1.0) - std::f64::consts::E).abs() < 1e-14);
}
#[test]
fn test_exp_nan() {
assert!(math_exp(f64::NAN).is_nan());
}
#[test]
fn test_exp_infinity() {
assert_eq!(math_exp(f64::INFINITY), f64::INFINITY);
assert_eq!(math_exp(f64::NEG_INFINITY), 0.0);
}
#[test]
fn test_expm1_zero() {
assert_eq!(math_expm1(0.0), 0.0);
}
#[test]
fn test_expm1_nan() {
assert!(math_expm1(f64::NAN).is_nan());
}
#[test]
fn test_log1p_zero() {
assert_eq!(math_log1p(0.0), 0.0);
}
#[test]
fn test_log1p_minus_one() {
assert_eq!(math_log1p(-1.0), f64::NEG_INFINITY);
}
#[test]
fn test_log1p_nan() {
assert!(math_log1p(f64::NAN).is_nan());
}
#[test]
fn test_sinh_zero() {
assert_eq!(math_sinh(0.0), 0.0);
}
#[test]
fn test_cosh_zero() {
assert_eq!(math_cosh(0.0), 1.0);
}
#[test]
fn test_tanh_zero() {
assert_eq!(math_tanh(0.0), 0.0);
}
#[test]
fn test_sinh_nan() {
assert!(math_sinh(f64::NAN).is_nan());
}
#[test]
fn test_cosh_nan() {
assert!(math_cosh(f64::NAN).is_nan());
}
#[test]
fn test_tanh_nan() {
assert!(math_tanh(f64::NAN).is_nan());
}
#[test]
fn test_asinh_zero() {
assert_eq!(math_asinh(0.0), 0.0);
}
#[test]
fn test_acosh_one() {
assert!((math_acosh(1.0) - 0.0).abs() < 1e-15);
}
#[test]
fn test_acosh_less_than_one() {
assert!(math_acosh(0.5).is_nan());
}
#[test]
fn test_atanh_zero() {
assert_eq!(math_atanh(0.0), 0.0);
}
#[test]
fn test_atanh_out_of_range() {
assert!(math_atanh(2.0).is_nan());
}
#[test]
fn test_math_max_empty_returns_neg_infinity() {
assert_eq!(math_max(&[]), f64::NEG_INFINITY);
}
#[test]
fn test_math_min_empty_returns_pos_infinity() {
assert_eq!(math_min(&[]), f64::INFINITY);
}
#[test]
fn test_math_pow_neg1_pos_infinity_is_nan() {
assert!(math_pow(-1.0, f64::INFINITY).is_nan());
}
#[test]
fn test_math_pow_1_neg_infinity_is_nan() {
assert!(math_pow(1.0, f64::NEG_INFINITY).is_nan());
}
#[test]
fn test_math_round_neg_half_is_neg_zero() {
let r = math_round(-0.5);
assert_eq!(r, 0.0);
assert!(r.is_sign_negative());
}
#[test]
fn test_math_pow_zero_exponent() {
assert_eq!(math_pow(f64::NAN, 0.0), 1.0);
assert_eq!(math_pow(f64::INFINITY, 0.0), 1.0);
}
#[test]
fn test_math_pow_base_2_exponent_10() {
assert!((math_pow(2.0, 10.0) - 1024.0).abs() < 1e-10);
}
}