diff --git a/src/fp_utils.rs b/src/fp_utils.rs index 15f1c49..0cc3645 100644 --- a/src/fp_utils.rs +++ b/src/fp_utils.rs @@ -4,6 +4,7 @@ //! From Theory to Implementation" 3rd edition by Pharr et al. use crate::math::{dot, Normal, Point, Vector}; +pub use rmath::utils::{decrement_ulp, increment_ulp}; #[inline(always)] pub fn fp_gamma(n: u32) -> f32 { @@ -12,36 +13,6 @@ pub fn fp_gamma(n: u32) -> f32 { (e * n as f32) / (1.0 - (e * n as f32)) } -pub fn increment_ulp(v: f32) -> f32 { - if v.is_finite() { - if v > 0.0 { - f32::from_bits(v.to_bits() + 1) - } else if v < -0.0 { - f32::from_bits(v.to_bits() - 1) - } else { - f32::from_bits(0x00_00_00_01) - } - } else { - // Infinity or NaN. - v - } -} - -pub fn decrement_ulp(v: f32) -> f32 { - if v.is_finite() { - if v > 0.0 { - f32::from_bits(v.to_bits() - 1) - } else if v < -0.0 { - f32::from_bits(v.to_bits() + 1) - } else { - f32::from_bits(0x80_00_00_01) - } - } else { - // Infinity or NaN. - v - } -} - pub fn robust_ray_origin(pos: Point, pos_err: f32, nor: Normal, ray_dir: Vector) -> Point { // Get surface normal pointing in the same // direction as ray_dir. @@ -81,51 +52,7 @@ pub fn robust_ray_origin(pos: Point, pos_err: f32, nor: Normal, ray_dir: Vector) Point::new(x, y, z) } -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn inc_ulp() { - assert!(increment_ulp(1.0) > 1.0); - assert!(increment_ulp(-1.0) > -1.0); - } - - #[test] - fn dec_ulp() { - assert!(decrement_ulp(1.0) < 1.0); - assert!(decrement_ulp(-1.0) < -1.0); - } - - #[test] - fn inc_ulp_zero() { - assert!(increment_ulp(0.0) > 0.0); - assert!(increment_ulp(0.0) > -0.0); - assert!(increment_ulp(-0.0) > 0.0); - assert!(increment_ulp(-0.0) > -0.0); - } - - #[test] - fn dec_ulp_zero() { - assert!(decrement_ulp(0.0) < 0.0); - assert!(decrement_ulp(0.0) < -0.0); - assert!(decrement_ulp(-0.0) < 0.0); - assert!(decrement_ulp(-0.0) < -0.0); - } - - #[test] - fn inc_dec_ulp() { - assert_eq!(decrement_ulp(increment_ulp(1.0)), 1.0); - assert_eq!(decrement_ulp(increment_ulp(-1.0)), -1.0); - assert_eq!(decrement_ulp(increment_ulp(1.2)), 1.2); - assert_eq!(decrement_ulp(increment_ulp(-1.2)), -1.2); - } - - #[test] - fn dec_inc_ulp() { - assert_eq!(increment_ulp(decrement_ulp(1.0)), 1.0); - assert_eq!(increment_ulp(decrement_ulp(-1.0)), -1.0); - assert_eq!(increment_ulp(decrement_ulp(1.2)), 1.2); - assert_eq!(increment_ulp(decrement_ulp(-1.2)), -1.2); - } -} +// #[cfg(test)] +// mod tests { +// use super::*; +// } diff --git a/sub_crates/rmath/src/utils.rs b/sub_crates/rmath/src/utils.rs index da746f8..e6cbcd9 100644 --- a/sub_crates/rmath/src/utils.rs +++ b/sub_crates/rmath/src/utils.rs @@ -9,7 +9,7 @@ #[inline(always)] pub fn ulp_diff(a: f32, b: f32) -> u32 { const TOP_BIT: u32 = 1 << 31; - const NAN_THRESHOLD: u32 = 0x7f800000; + const INFINITY: u32 = 0x7f800000; let a = a.to_bits(); let b = b.to_bits(); @@ -19,7 +19,7 @@ pub fn ulp_diff(a: f32, b: f32) -> u32 { let a_abs = a & !TOP_BIT; let b_abs = b & !TOP_BIT; - if a_abs > NAN_THRESHOLD || b_abs > NAN_THRESHOLD { + if a_abs > INFINITY || b_abs > INFINITY { // NaNs always return maximum ulps apart. u32::MAX } else if a_sign == b_sign { @@ -36,6 +36,56 @@ pub fn ulps_eq(a: f32, b: f32, max_ulps: u32) -> bool { ulp_diff(a, b) <= max_ulps.min(u32::MAX - 1) } +/// Increments to the next representable floating point number. +/// +/// Notes: +/// - 0.0 and -0.0 are treated as the same value. E.g. starting from the +/// number just before -0.0, it only takes two increments to get to the +/// number just after 0.0. +/// - Infinity, NaN, and their negative counterparts are returned +/// unchanged. +/// - Incrementing `f32::MAX` results in infinity. +#[inline(always)] +pub fn increment_ulp(v: f32) -> f32 { + if v.is_finite() { + if v > 0.0 { + f32::from_bits(v.to_bits() + 1) + } else if v < -0.0 { + f32::from_bits(v.to_bits() - 1) + } else { + f32::from_bits(1) + } + } else { + // Infinity or NaN. + v + } +} + +/// Decrements to the previous representable floating point number. +/// +/// Notes: +/// - 0.0 and -0.0 are treated as the same value. E.g. starting from the +/// number just after 0.0, it only takes two decrements to get to the +/// number just before -0.0. +/// - Infinity, NaN, and their negative counterparts are returned +/// unchanged. +/// - Decrementing `-f32::MAX` results in -infinity. +#[inline(always)] +pub fn decrement_ulp(v: f32) -> f32 { + if v.is_finite() { + if v > 0.0 { + f32::from_bits(v.to_bits() - 1) + } else if v < -0.0 { + f32::from_bits(v.to_bits() + 1) + } else { + f32::from_bits(0x80000001) + } + } else { + // Infinity or NaN. + v + } +} + #[cfg(test)] mod tests { use super::*; @@ -93,4 +143,54 @@ mod tests { assert!(!ulps_eq(std::f32::NAN, std::f32::INFINITY, 1 << 31)); assert!(!ulps_eq(std::f32::INFINITY, std::f32::NAN, 1 << 31)); } + + #[test] + fn inc_ulp() { + assert!(increment_ulp(1.0) > 1.0); + assert!(increment_ulp(-1.0) > -1.0); + assert!(increment_ulp(0.0) > 0.0); + assert!(increment_ulp(0.0) > -0.0); + assert!(increment_ulp(-0.0) > 0.0); + assert!(increment_ulp(-0.0) > -0.0); + assert!(increment_ulp(f32::MAX) == f32::INFINITY); + assert!(increment_ulp(f32::INFINITY) == f32::INFINITY); + assert!(increment_ulp(-f32::INFINITY) == -f32::INFINITY); + assert!(increment_ulp(f32::NAN).is_nan()); + assert!(increment_ulp(-f32::NAN).is_nan()); + } + + #[test] + fn dec_ulp() { + assert!(decrement_ulp(1.0) < 1.0); + assert!(decrement_ulp(-1.0) < -1.0); + assert!(decrement_ulp(0.0) < 0.0); + assert!(decrement_ulp(0.0) < -0.0); + assert!(decrement_ulp(-0.0) < 0.0); + assert!(decrement_ulp(-0.0) < -0.0); + assert!(decrement_ulp(f32::MIN) == -f32::INFINITY); + assert!(decrement_ulp(f32::INFINITY) == f32::INFINITY); + assert!(decrement_ulp(-f32::INFINITY) == -f32::INFINITY); + assert!(decrement_ulp(f32::NAN).is_nan()); + assert!(decrement_ulp(-f32::NAN).is_nan()); + } + + #[test] + fn inc_dec_ulp() { + assert_eq!(decrement_ulp(increment_ulp(0.0)), 0.0); + assert_eq!(decrement_ulp(increment_ulp(-0.0)), 0.0); + assert_eq!(decrement_ulp(increment_ulp(1.0)), 1.0); + assert_eq!(decrement_ulp(increment_ulp(-1.0)), -1.0); + assert_eq!(decrement_ulp(increment_ulp(1.2)), 1.2); + assert_eq!(decrement_ulp(increment_ulp(-1.2)), -1.2); + } + + #[test] + fn dec_inc_ulp() { + assert_eq!(increment_ulp(decrement_ulp(0.0)), 0.0); + assert_eq!(increment_ulp(decrement_ulp(-0.0)), 0.0); + assert_eq!(increment_ulp(decrement_ulp(1.0)), 1.0); + assert_eq!(increment_ulp(decrement_ulp(-1.0)), -1.0); + assert_eq!(increment_ulp(decrement_ulp(1.2)), 1.2); + assert_eq!(increment_ulp(decrement_ulp(-1.2)), -1.2); + } }