use super::exp::simd_exp;
use super::traits::SimdFloat;
#[inline]
pub fn simd_log<S: SimdFloat>(x: S) -> S {
let tiny = S::splat(1.175494e-38);
let x = x.max(tiny);
let mut buf = [0.0f32; 16];
unsafe {
x.store(buf.as_mut_ptr());
}
let mut mantissa_buf = [0.0f32; 16];
let mut exponent_buf = [0.0f32; 16];
let sqrt2 = core::f32::consts::SQRT_2;
for i in 0..S::WIDTH {
let bits = buf[i].to_bits();
let mut e = ((bits >> 23) & 0xFF) as i32 - 127;
let m_bits = (bits & 0x007F_FFFF) | 0x3F80_0000;
let mut m = f32::from_bits(m_bits);
if m >= sqrt2 {
m *= 0.5;
e += 1;
}
mantissa_buf[i] = m;
exponent_buf[i] = e as f32;
}
let m = unsafe { S::load(mantissa_buf.as_ptr()) };
let e = unsafe { S::load(exponent_buf.as_ptr()) };
let f = m.sub(S::one());
let two = S::splat(2.0);
let s = f.div(m.add(S::one())); let s2 = s.mul(s);
let c1 = S::one();
let c3 = S::splat(1.0 / 3.0);
let c5 = S::splat(1.0 / 5.0);
let c7 = S::splat(1.0 / 7.0);
let c9 = S::splat(1.0 / 9.0);
let c11 = S::splat(1.0 / 11.0);
let q = c11.fma(s2, c9);
let q = q.fma(s2, c7);
let q = q.fma(s2, c5);
let q = q.fma(s2, c3);
let q = q.fma(s2, c1);
let p = two.mul(s).mul(q);
let ln2 = S::splat(core::f32::consts::LN_2);
e.fma(ln2, p)
}
#[inline]
pub fn simd_log1pexp<S: SimdFloat>(x: S) -> S {
let abs_x = x.abs();
let max_x_0 = x.max(S::zero());
let neg_abs_x = abs_x.neg();
let exp_neg = simd_exp(neg_abs_x);
let one_plus = exp_neg.add(S::one());
let log_part = simd_log(one_plus);
max_x_0.add(log_part)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::simd::generic::ScalarFloat;
#[test]
fn test_log_accuracy() {
let n = 1_000_000;
let mut max_rel_err: f32 = 0.0;
for i in 1..n {
let x = 0.001 + 20.0 * (i as f32) / (n as f32);
let expected = x.ln();
let result = simd_log(ScalarFloat(x)).0;
let abs_err = (result - expected).abs();
let rel_err = if expected.abs() > 1e-6 {
abs_err / expected.abs()
} else {
abs_err
};
max_rel_err = max_rel_err.max(rel_err);
}
assert!(
max_rel_err < 1e-5,
"log max relative error {max_rel_err} exceeds 1e-5"
);
}
#[test]
fn test_log_edge_cases() {
let r = simd_log(ScalarFloat(1.0)).0;
assert!(r.abs() < 1e-6, "ln(1) = {r}");
let r = simd_log(ScalarFloat(core::f32::consts::E)).0;
assert!((r - 1.0).abs() < 1e-5, "ln(e) = {r}");
let r = simd_log(ScalarFloat(1e-30)).0;
assert!(r.is_finite(), "ln(1e-30) should be finite");
let r = simd_log(ScalarFloat(0.0)).0;
assert!(r.is_finite(), "ln(0) should be finite (clamped)");
}
#[test]
fn test_log1pexp_accuracy() {
let n = 100_000;
let mut max_abs_err: f32 = 0.0;
for i in 0..n {
let x = -10.0 + 20.0 * (i as f32) / (n as f32);
let expected = (1.0 + x.exp()).ln();
let result = simd_log1pexp(ScalarFloat(x)).0;
let abs_err = (result - expected).abs();
max_abs_err = max_abs_err.max(abs_err);
}
assert!(
max_abs_err < 1e-4,
"log1pexp max abs error {max_abs_err} exceeds 1e-4"
);
}
#[test]
fn test_log1pexp_large_values() {
let r = simd_log1pexp(ScalarFloat(50.0)).0;
assert!((r - 50.0).abs() < 0.01, "log1pexp(50) = {r}");
let r = simd_log1pexp(ScalarFloat(-50.0)).0;
assert!(r.abs() < 0.001 && r >= 0.0, "log1pexp(-50) = {r}");
let r = simd_log1pexp(ScalarFloat(0.0)).0;
assert!(
(r - core::f32::consts::LN_2).abs() < 1e-4,
"log1pexp(0) = {r}"
);
}
}