#[inline]
pub fn sqrt_f32(x: f32) -> f32 {
if x <= 0.0 || x < 1e-10 {
return 0.0;
}
let mut g = x * 0.5;
for _ in 0..12 {
let next = 0.5 * (g + x / g);
if (next - g).abs() < g * 1.2e-7 {
return next;
}
g = next;
}
g
}
#[inline]
pub fn exp_f32(x: f32) -> f32 {
if x >= 88.0 { return f32::MAX; }
if x <= -87.0 { return 0.0; }
const LN2: f32 = 0.693_147_18;
const LN2_INV: f32 = 1.442_695_04;
let n = floor_f32(x * LN2_INV + 0.5) as i32;
let r = x - n as f32 * LN2;
let p = 1.0 + r * (1.0 + r * (
0.5
+ r * (1.666_666_7e-1
+ r * (4.166_666_7e-2
+ r * (8.333_333e-3
+ r * 1.388_889e-3)))));
let pow2n = f32::from_bits(((n + 127) as u32).wrapping_shl(23));
p * pow2n
}
#[inline]
pub fn floor_f32(x: f32) -> f32 {
let i = x as i32;
let fi = i as f32;
if x < fi { fi - 1.0 } else { fi }
}
#[inline]
pub fn round_f32(x: f32) -> f32 {
if x >= 0.0 {
floor_f32(x + 0.5)
} else {
-floor_f32(-x + 0.5)
}
}
#[inline]
pub fn ln_f32(x: f32) -> f32 {
if x <= 0.0 {
return f32::NEG_INFINITY;
}
let bits = x.to_bits();
let exp_biased = ((bits >> 23) & 0xFF) as i32;
let (e, m) = if exp_biased == 0 {
let leading = (bits << 9).leading_zeros() as i32;
let eff_exp = -126 - leading;
let m = f32::from_bits((bits << (leading as u32 + 1) & 0x007F_FFFF) | 0x3F80_0000);
(eff_exp, m)
} else {
let e = exp_biased - 127;
let m = f32::from_bits((bits & 0x007F_FFFF) | 0x3F80_0000);
(e, m)
};
let (m2, e2) = if m > 1.414_213_5_f32 {
(m * 0.5, e + 1)
} else {
(m, e)
};
let u = m2 - 1.0;
let p = u * (1.0
+ u * (-0.5
+ u * (0.333_333_3
+ u * (-0.25
+ u * (0.2
+ u * (-0.166_666_7))))));
p + (e2 as f32) * core::f32::consts::LN_2
}
#[inline]
pub fn mean_f32(xs: &[f32]) -> f32 {
if xs.is_empty() { return 0.0; }
xs.iter().sum::<f32>() / xs.len() as f32
}
#[inline]
pub fn std_dev_f32(xs: &[f32]) -> f32 {
if xs.len() < 2 { return 0.0; }
let m = mean_f32(xs);
let var = xs.iter().map(|&x| (x - m) * (x - m)).sum::<f32>() / xs.len() as f32;
sqrt_f32(var)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn sqrt_known_values() {
assert!((sqrt_f32(4.0) - 2.0).abs() < 1e-5, "sqrt(4)");
assert!((sqrt_f32(9.0) - 3.0).abs() < 1e-5, "sqrt(9)");
assert!((sqrt_f32(0.01) - 0.1).abs() < 1e-4, "sqrt(0.01)");
assert!((sqrt_f32(2.0) - 1.41421356).abs() < 1e-5, "sqrt(2)");
assert_eq!(sqrt_f32(0.0), 0.0, "sqrt(0)");
assert_eq!(sqrt_f32(-1.0), 0.0, "sqrt(-1)");
assert_eq!(sqrt_f32(1e-11), 0.0, "sub-epsilon returns 0");
}
#[test]
fn mean_basic() {
let xs = [1.0f32, 2.0, 3.0, 4.0, 5.0];
assert!((mean_f32(&xs) - 3.0).abs() < 1e-5);
}
#[test]
fn std_dev_zero_for_constant() {
let xs = [0.05f32; 50];
assert!(std_dev_f32(&xs) < 1e-4, "std_dev of constant must be ~0");
}
#[test]
fn std_dev_known() {
let xs = [0.0f32, 1.0, 2.0, 3.0, 4.0];
let s = std_dev_f32(&xs);
assert!((s - 1.41421).abs() < 1e-3, "std_dev={}", s);
}
#[test]
fn exp_zero_is_one() {
assert!((exp_f32(0.0) - 1.0).abs() < 1e-6, "e^0 = 1");
}
#[test]
fn exp_one() {
assert!((exp_f32(1.0) - 2.718_282).abs() < 1e-4, "e^1");
}
#[test]
fn exp_minus_one() {
assert!((exp_f32(-1.0) - 0.367_879).abs() < 1e-4, "e^-1");
}
#[test]
fn exp_ln2_is_two() {
assert!((exp_f32(0.693_147) - 2.0).abs() < 1e-4, "e^ln2 = 2");
}
#[test]
fn exp_negative_large() {
assert!((exp_f32(-5.0) - 0.006_738).abs() < 1e-4, "e^-5");
}
#[test]
fn exp_positive_large() {
assert!((exp_f32(10.0) - 22026.47).abs() < 10.0, "e^10");
}
#[test]
fn exp_clamp_overflow() {
assert_eq!(exp_f32(100.0), f32::MAX, "overflow clamp");
}
#[test]
fn exp_clamp_underflow() {
assert_eq!(exp_f32(-100.0), 0.0, "underflow clamp");
}
#[test]
fn ln_one_is_zero() {
assert!((ln_f32(1.0)).abs() < 1e-6, "ln(1) = 0");
}
#[test]
fn ln_e_is_one() {
use core::f32::consts::E;
assert!((ln_f32(E) - 1.0).abs() < 1e-4, "ln(e) ≈ 1");
}
#[test]
fn ln_two() {
assert!((ln_f32(2.0) - 0.693_147).abs() < 1e-4, "ln(2)");
}
#[test]
fn ln_inverse_of_exp() {
for i in -5_i32..=5 {
let x = i as f32;
let roundtrip = ln_f32(exp_f32(x));
assert!((roundtrip - x).abs() < 1e-3,
"ln(exp({})) = {} (expected {})", x, roundtrip, x);
}
}
#[test]
fn ln_negative_is_neg_infinity() {
assert_eq!(ln_f32(-1.0), f32::NEG_INFINITY);
assert_eq!(ln_f32(0.0), f32::NEG_INFINITY);
}
#[test]
fn ln_large_value() {
assert!((ln_f32(1000.0) - 6.907_755).abs() < 0.01, "ln(1000)");
}
}