use rustebra::scalar::Scalar;
const FIXTURE_F64: &str = include_str!("fixtures/elementary_reference_f64.csv");
const FIXTURE_F32: &str = include_str!("fixtures/elementary_reference_f32.csv");
const ZERO_CROSSING_FLOOR: f64 = 1e-9;
struct Row {
function: &'static str,
input: f64,
expected: f64,
}
fn parse_fixture(fixture: &'static str) -> Vec<Row> {
fixture
.lines()
.skip(1)
.filter(|line| !line.is_empty())
.map(|line| {
let mut fields = line.split(',');
let function = fields.next().unwrap();
let input: f64 = fields.next().unwrap().parse().unwrap();
let expected: f64 = fields.next().unwrap().parse().unwrap();
Row {
function,
input,
expected,
}
})
.collect()
}
fn error(actual: f64, expected: f64) -> f64 {
if expected.abs() < ZERO_CROSSING_FLOOR {
(actual - expected).abs()
} else {
(actual - expected).abs() / expected.abs()
}
}
fn check_f64(function: &str, input: f64, expected: f64, tol: f64) -> Option<String> {
let actual = match function {
"sqrt" => Scalar::sqrt(input),
"sin" => Scalar::sin(input),
"cos" => Scalar::cos(input),
other => panic!("unknown function in fixture: {other}"),
};
let err = error(actual, expected);
if err > tol {
Some(format!(
"f64::{function}({input:e}): got {actual:e}, expected {expected:e}, error {err:e} > {tol:e}"
))
} else {
None
}
}
fn check_f32(function: &str, input: f32, expected: f32, tol: f32) -> Option<String> {
let actual = match function {
"sqrt" => Scalar::sqrt(input),
"sin" => Scalar::sin(input),
"cos" => Scalar::cos(input),
other => panic!("unknown function in fixture: {other}"),
};
let err = if expected.abs() < ZERO_CROSSING_FLOOR as f32 {
(actual - expected).abs()
} else {
(actual - expected).abs() / expected.abs()
};
if err > tol {
Some(format!(
"f32::{function}({input:e}): got {actual:e}, expected {expected:e}, error {err:e} > {tol:e}"
))
} else {
None
}
}
#[test]
fn f64_elementary_functions_meet_the_relative_error_target_over_the_documented_domain() {
const TOL: f64 = 1e-14;
let rows = parse_fixture(FIXTURE_F64);
let failures: Vec<String> = rows
.iter()
.filter_map(|row| check_f64(row.function, row.input, row.expected, TOL))
.collect();
assert!(
failures.is_empty(),
"{} of {} f64 samples exceeded the 1e-14 target:\n{}",
failures.len(),
rows.len(),
failures.join("\n")
);
}
#[test]
fn f32_elementary_functions_meet_the_relative_error_target_over_the_documented_domain() {
const TOL: f32 = 1e-6;
let rows = parse_fixture(FIXTURE_F32);
let failures: Vec<String> = rows
.iter()
.filter_map(|row| check_f32(row.function, row.input as f32, row.expected as f32, TOL))
.collect();
assert!(
failures.is_empty(),
"{} of {} f32 samples exceeded the 1e-6 target:\n{}",
failures.len(),
rows.len(),
failures.join("\n")
);
}