use deki_macros::imp;
use extension_traits::extension as ext;
#[allow(clippy::excessive_precision, clippy::approx_constant)]
#[imp(*DekiExtApprox)]
impl f32 {
#[inline]
fn sqrt_fast(self) -> f32 {
if self <= 0.0 { return 0.0; }
let bits = self.to_bits();
let guess = f32::from_bits(0x1fbd1daf + (bits >> 1));
guess + (self / guess - guess) * 0.5
}
#[inline]
fn exp_fast(self) -> f32 {
pow2_fast(1.442695040_f32 * self) }
#[inline]
fn pow_fast(self, b: f32) -> f32 {
pow2_fast(b * self.log2_fast())
}
#[inline]
fn log2_fast(self) -> f32 {
let vx = self.to_bits();
let mx = f32::from_bits((vx & 0x007FFFFF_u32) | 0x3f000000);
let mut y = vx as f32;
y *= 1.1920928955078125e-7_f32;
y - 124.22551499_f32 - 1.498030302_f32 * mx - 1.72587999_f32 / (0.3520887068_f32 + mx)
}
}
#[allow(clippy::excessive_precision, clippy::approx_constant)]
#[inline]
fn pow2_fast(p: f32) -> f32 {
let clipp = if p < -126.0 { -126.0_f32 } else { p };
let v = ((1 << 23) as f32 * (clipp + 126.94269504_f32)) as u32;
f32::from_bits(v)
}
#[cfg(test)]
#[allow(clippy::excessive_precision, clippy::approx_constant)]
mod tests {
use super::DekiExtApprox;
fn approx_eq(a: f32, b: f32, max_abs: f32, max_rel: f32) -> bool {
let diff = (a - b).abs();
diff <= max_abs || diff / b.abs() <= max_rel
}
#[test]
fn sqrt_fast_close_to_std() {
for i in 1..=100 {
let x = (i as f32) * 0.1;
let expected = x.sqrt();
let got = x.sqrt_fast();
assert!(
approx_eq(got, expected, 1e-2, 7e-2),
"sqrt_fast({}) = {} (expected {})",
x, got, expected
);
}
}
#[test]
fn exp_fast_close_to_std() {
for i in -10..=10 {
let x = (i as f32) * 0.1;
let expected = x.exp();
let got = x.exp_fast();
assert!(
approx_eq(got, expected, 5e-2, 5e-2),
"exp_fast({}) = {} (expected {})",
x, got, expected
);
}
}
#[test]
fn pow_fast_close_to_std() {
for i in 1..=50 {
let base = (i as f32) * 0.2;
let exp = 2.0;
let expected = base.powf(exp);
let got = base.pow_fast(exp);
assert!(
approx_eq(got, expected, 1.0, 7e-2),
"pow_fast({}, {}) = {} (expected {})",
base, exp, got, expected
);
}
}
#[test]
fn log2_fast_close_to_std() {
for i in 1..=100 {
let x = (i as f32) * 0.1;
let expected = x.log2();
let got = x.log2_fast();
assert!(
approx_eq(got, expected, 1e-1, 6e-2),
"log2_fast({}) = {} (expected {})",
x, got, expected
);
}
}
#[test]
fn pow_fast_identity() {
assert!((10.0_f32.pow_fast(0.0) - 1.0).abs() < 0.1);
}
}