logs.rs

2.3 kB · rust · 90 lines

1use std::f64::consts::LN_2;23const MANTISSA: u64 = 0x000f_ffff_ffff_ffff;4const ONE_EXPONENT: u64 = 0x3ff0_0000_0000_0000;5const EXPONENT: u64 = 0x7ff;6const BIAS: i32 = 1023;7const SCALE: f64 = 4_503_599_627_370_496.0;8const TERMS: usize = 16;910/// Returns the natural logarithm of a number, from a series instead of libm.11pub fn ln(x: f64) -> f64 {12    if x.is_nan() || x < 0.0 {13        return f64::NAN;14    }15    if x == 0.0 {16        return f64::NEG_INFINITY;17    }18    if x.is_infinite() {19        return x;20    }21    let mut bits = x.to_bits();22    let mut e: i32 = 0;23    if (bits >> 52) & EXPONENT == 0 {24        bits = (x * SCALE).to_bits();25        e -= 52;26    }27    e += (((bits >> 52) & EXPONENT) as i32) - BIAS;28    let mut m = f64::from_bits((bits & MANTISSA) | ONE_EXPONENT);29    if m >= 1.5 {30        m *= 0.5;31        e += 1;32    }33    let s = (m - 1.0) / (m + 1.0);34    let square = s * s;35    let mut term = s;36    let mut sum = 0.0;37    for k in 0..TERMS {38        sum += term / (2 * k + 1) as f64;39        term *= square;40    }41    e as f64 * LN_2 + 2.0 * sum42}4344#[cfg(test)]45mod tests {46    use super::*;4748    #[test]49    fn one_and_two_are_exact() {50        assert_eq!(ln(1.0).to_bits(), 0.0f64.to_bits());51        assert_eq!(ln(2.0), LN_2);52    }5354    #[test]55    fn the_edges_and_the_subnormals_hold() {56        assert!(ln(f64::NAN).is_nan());57        assert!(ln(-1.0).is_nan());58        assert_eq!(ln(0.0), f64::NEG_INFINITY);59        assert_eq!(ln(-0.0), f64::NEG_INFINITY);60        assert!(ln(f64::NEG_INFINITY).is_nan());61        assert_eq!(ln(f64::INFINITY), f64::INFINITY);62        assert_eq!(ln(f64::from_bits(1)), -1074.0 * LN_2);63    }6465    #[test]66    fn the_integers_track_libm() {67        for i in 2..=100_000u32 {68            let x = i as f64;69            let want = x.ln();70            assert!(71                (ln(x) - want).abs() <= 4.0 * f64::EPSILON * want.abs(),72                "ln({x}) is {} not {want}",73                ln(x)74            );75        }76    }7778    #[test]79    fn the_powers_of_two_track_libm() {80        for k in -300..=300i32 {81            let x = f64::from_bits(((BIAS + k) as u64) << 52);82            let want = x.ln();83            assert!(84                (ln(x) - want).abs() <= 4.0 * f64::EPSILON * want.abs(),85                "ln(2^{k}) is {} not {want}",86                ln(x)87            );88        }89    }90}