#![cfg(table_format = "q16_16")]
use g_math::fixed_point::imperative::FixedPoint;
fn fp(s: &str) -> FixedPoint { FixedPoint::from_str(s) }
const Q8_24_REFS: &[(&str, i32, i32, &str)] = &[
("0.5", 8388608, 27660953, "exp"),
("1.0", 16777216, 45605201, "exp"),
("-1.0", -16777216, 6171993, "exp"),
("2.0", 33554432, 123967790, "exp"),
("0.1", 1677722, 18541691, "exp"),
("0.5", 8388608, -11629080, "ln"),
("2.0", 33554432, 11629080, "ln"),
("3.0", 50331648, 18431656, "ln"),
("0.1", 1677722, -38630963, "ln"), ("10.0", 167772160, 38630967, "ln"),
("0.5", 8388608, 8043426, "sin"),
("1.0", 16777216, 14117540, "sin"),
("2.0", 33554432, 15255479, "sin"),
("3.0", 50331648, 2367601, "sin"),
("0.1", 1677722, 1674927, "sin"),
("0.5", 8388608, 14723392, "cos"),
("1.0", 16777216, 9064768, "cos"),
("2.0", 33554432, -6981785, "cos"),
("3.0", 50331648, -16609318, "cos"),
("0.1", 1677722, 16693400, "cos"),
("0.5", 8388608, 11863283, "sqrt"),
("2.0", 33554432, 23726566, "sqrt"),
("4.0", 67108864, 33554432, "sqrt"),
("9.0", 150994944, 50331648, "sqrt"),
("0.1", 1677722, 5305422, "sqrt"),
("0.5", 8388608, 7778716, "atan"),
("1.0", 16777216, 13176795, "atan"),
("2.0", 33554432, 18574873, "atan"),
("-1.0", -16777216, -13176795, "atan"),
("0.1", 1677722, 1672163, "atan"),
];
const ROPE_REFS: &[(i64, i32, i32)] = &[
(42949672960000, -5127359, -15974516),
(429496729600000, 599765, -16766492),
(4294967296000000, -5871917, 15716093),
];
#[test]
fn test_q8_24_transcendentals_vs_mpmath() {
let one = FixedPoint::one();
let one_raw = one.raw();
if one_raw != 1i32 << 24 {
println!("SKIP: FRAC_BITS is not 24 (one.raw() = {}, expected {})", one_raw, 1i32 << 24);
println!("Run with: GMATH_PROFILE=realtime GMATH_FRAC_BITS=24 cargo test --test q8_24_validation");
return; }
let mut failures = Vec::new();
let mut max_ulp: i32 = 0;
for &(input, _input_raw, expected_raw, func) in Q8_24_REFS {
let input_fp = fp(input);
let result = match func {
"exp" => input_fp.try_exp(),
"ln" => input_fp.try_ln(),
"sin" => input_fp.try_sin(),
"cos" => input_fp.try_cos(),
"sqrt" => input_fp.try_sqrt(),
"atan" => input_fp.try_atan(),
_ => panic!("unknown function: {}", func),
};
match result {
Ok(val) => {
let got_raw = val.raw();
let ulp = (got_raw - expected_raw).abs();
if ulp > max_ulp { max_ulp = ulp; }
if ulp > 1 {
failures.push(format!(" {}({}) = {} (expected {}), {} ULP",
func, input, got_raw, expected_raw, ulp));
}
}
Err(e) => {
failures.push(format!(" {}({}) = ERROR: {:?}", func, input, e));
}
}
}
println!("\n=== Q8.24 Realtime Profile ULP Validation ===");
println!("Total test points: {}", Q8_24_REFS.len());
println!("Max ULP: {}", max_ulp);
println!("Failures (>1 ULP): {}", failures.len());
for f in &failures {
println!("{}", f);
}
assert!(failures.is_empty(),
"Q8.24 profile: {} failures out of {} tests (max ULP: {})\n{}",
failures.len(), Q8_24_REFS.len(), max_ulp, failures.join("\n"));
}
#[test]
fn test_sincos_wide_rope_angles() {
let one = FixedPoint::one();
if one.raw() != 1i32 << 24 {
println!("SKIP: sincos_wide RoPE test requires Q8.24 (GMATH_FRAC_BITS=24)");
return;
}
println!("\n=== sincos_wide RoPE Validation ===");
let mut max_ulp: i32 = 0;
let mut failures = Vec::new();
for &(angle_q32, expected_sin, expected_cos) in ROPE_REFS {
let (sin_val, cos_val) = FixedPoint::sincos_wide(angle_q32);
let sin_ulp = (sin_val.raw() - expected_sin).abs();
let cos_ulp = (cos_val.raw() - expected_cos).abs();
let ulp = sin_ulp.max(cos_ulp);
if ulp > max_ulp { max_ulp = ulp; }
let angle_int = angle_q32 >> 32;
println!(" sincos_wide({}) → sin={}, cos={} (sin_ulp={}, cos_ulp={})",
angle_int, sin_val.raw(), cos_val.raw(), sin_ulp, cos_ulp);
if ulp > 1 {
failures.push(format!(" angle={}: sin_ulp={}, cos_ulp={}", angle_int, sin_ulp, cos_ulp));
}
}
println!("Max ULP: {}", max_ulp);
assert!(failures.is_empty(),
"sincos_wide: {} failures, max ULP {}\n{}", failures.len(), max_ulp, failures.join("\n"));
}