sleef 0.3.3

Math functions for SIMD vectors
Documentation
use super::*;

use rug::{Assign, Float};
pub const PRECF64: u32 = 128;

pub fn count_ulp(d: f64, c: &Float) -> f64 {
    let c2 = c.to_f64();

    if (c2 == 0. || c2.is_subnormal()) && (d == 0. || d.is_subnormal()) {
        return 0.;
    }

    if (c2 == 0.) && (d != 0.) {
        return 10000.;
    }

    if c2.is_infinite() && d.is_infinite() {
        return 0.;
    }

    let prec = c.prec();

    let mut fry = Float::with_val(prec, d);

    let mut frw = Float::new(prec);

    let (_, e) = c.to_f64_exp();

    frw.assign(Float::u_exp(1, e - 53_i32));

    fry -= c;
    fry /= &frw;
    fabs(fry.to_f64())
}

pub fn gen_input(rng: &mut rand::rngs::ThreadRng, range: core::ops::RangeInclusive<f64>) -> f64 {
    use rand::Rng;
    let mut start = *range.start();
    if start == f64::MIN {
        start = -1e306;
    }
    let mut end = *range.end();
    if end == f64::MAX {
        end = 1e306;
    }
    rng.gen_range(start..=end)
}

pub fn test_f_f(
    fun_fx: fn(f64) -> f64,
    fun_f: fn(Float) -> Float,
    range: core::ops::RangeInclusive<f64>,
    ulp_ex: f64,
) {
    let mut rng = rand::thread_rng();
    for n in 0..crate::TEST_REPEAT {
        let input = gen_input(&mut rng, range.clone());
        let output = fun_fx(input);
        let expected = fun_f(Float::with_val(PRECF64, input));
        if expected.is_nan() && output.is_nan() {
            continue;
        }
        let ulp = count_ulp(output, &expected);
        assert!(
            ulp <= ulp_ex,
            "Iteration: {n}, Input: {input:e}, Output: {output}, Expected: {expected}, ULP: {ulp} > {}",
            ulp_ex
        );
    }
}

pub fn test_f_ff(
    fun_fx: fn(f64) -> (f64, f64),
    fun_f: fn(Float) -> (Float, Float),
    range: core::ops::RangeInclusive<f64>,
    ulp_ex: f64,
) {
    let mut rng = rand::thread_rng();
    for n in 0..crate::TEST_REPEAT {
        let input = gen_input(&mut rng, range.clone());
        let (output1, output2) = fun_fx(input);
        let (expected1, expected2) = fun_f(Float::with_val(PRECF64, input));
        if (expected1.is_nan() && output1.is_nan()) || (expected2.is_nan() && output2.is_nan()) {
            continue;
        }
        let ulp1 = count_ulp(output1, &expected1);
        let ulp2 = count_ulp(output2, &expected2);
        assert!(
            ulp1 <= ulp_ex && ulp2 <= ulp_ex,
                "Iteration: {n}, Input: {input:e}, Output: ({output1}, {output2}), Expected: ({expected1}, {expected2}), ULP: ({ulp1}, {ulp2}) > {}",
                ulp_ex
        );
    }
}

pub fn test_ff_f(
    fun_fx: fn(f64, f64) -> f64,
    fun_f: fn(Float, &Float) -> Float,
    range1: core::ops::RangeInclusive<f64>,
    range2: core::ops::RangeInclusive<f64>,
    ulp_ex: f64,
) {
    let mut rng = rand::thread_rng();
    for n in 0..crate::TEST_REPEAT {
        let input1 = gen_input(&mut rng, range1.clone());
        let input2 = gen_input(&mut rng, range2.clone());
        let output = fun_fx(input1, input2);
        let expected = fun_f(
            Float::with_val(PRECF64, input1),
            &Float::with_val(PRECF64, input2),
        );
        if expected.is_nan() && output.is_nan() {
            continue;
        }
        let ulp = count_ulp(output, &expected);
        assert!(
            ulp <= ulp_ex,
            "Iteration: {n}, Input: ({input1:e}, {input2:e}), Output: {output}, Expected: {expected}, ULP: {ulp} > {}",
            ulp_ex
        );
    }
}