diff --git a/Cargo.toml b/Cargo.toml index 513aa15..e0efbc3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "i_float" -version = "3.0.0" +version = "4.0.0" authors = ["Nail Sharipov "] edition = "2024" description = "This fixed float math library provides an efficient and deterministic solution for arithmetic and geometric operations." diff --git a/src/adapter.rs b/src/adapter.rs index 2a3cde1..7a130e4 100644 --- a/src/adapter.rs +++ b/src/adapter.rs @@ -115,17 +115,17 @@ impl FloatPointAdapter { rect: FloatRect, scale: P::Scalar, ) -> Result { - let scale_f64 = Self::validate_scale(scale)?; + let scale = Self::validate_scale(scale)?; let mut adapter = Self::new(rect); - let zero = P::Scalar::ZERO; - let is_degenerate = adapter.rect.width() == zero && adapter.rect.height() == zero; + let is_degenerate = + adapter.rect.width() == P::Scalar::ZERO && adapter.rect.height() == P::Scalar::ZERO; if !is_degenerate && adapter.dir_scale < scale { return Err(FloatPointAdapterScaleError::ScaleTooLarge); } adapter.dir_scale = scale; - adapter.inv_scale = P::Scalar::from_float(1.0 / scale_f64); + adapter.inv_scale = P::Scalar::ONE / scale; Ok(adapter) } @@ -157,15 +157,14 @@ impl FloatPointAdapter { } #[inline] - fn validate_scale(scale: P::Scalar) -> Result { - let scale_f64 = scale.to_f64(); - if !scale_f64.is_finite() { + fn validate_scale(scale: P::Scalar) -> Result { + if !scale.is_finite() { return Err(FloatPointAdapterScaleError::ScaleNotFinite); } - if scale_f64 <= 0.0 { + if scale <= P::Scalar::ZERO { return Err(FloatPointAdapterScaleError::ScaleNonPositive); } - Ok(scale_f64) + Ok(scale) } #[inline(always)] @@ -248,7 +247,7 @@ impl FloatPointAdapter { #[inline(always)] pub fn try_float_to_int(&self, point: &P) -> Result, FloatPointAdapterRangeError> { - if !self.rect.contains(point) { + if !point.is_finite() || !self.rect.contains(point) { return Err(FloatPointAdapterRangeError::PointOutOfRange); } @@ -444,6 +443,21 @@ mod tests { ); } + #[test] + fn test_try_float_to_int_not_finite() { + let adapter = + FloatPointAdapter::<[f64; 2], i32>::with_scale(FloatRect::new(-1.0, 1.0, -1.0, 1.0), 10.0); + + assert_eq!( + adapter.try_float_to_int(&[f64::NAN, 0.0]).err().unwrap(), + FloatPointAdapterRangeError::PointOutOfRange + ); + assert_eq!( + adapter.try_float_to_int(&[0.0, f64::INFINITY]).err().unwrap(), + FloatPointAdapterRangeError::PointOutOfRange + ); + } + #[test] fn test_try_int_to_float_range() { let adapter = diff --git a/src/float/compatible.rs b/src/float/compatible.rs index dfbd0e1..1889060 100644 --- a/src/float/compatible.rs +++ b/src/float/compatible.rs @@ -5,6 +5,11 @@ pub trait FloatPointCompatible: Copy { fn from_xy(x: Self::Scalar, y: Self::Scalar) -> Self; fn x(&self) -> Self::Scalar; fn y(&self) -> Self::Scalar; + + #[inline(always)] + fn is_finite(&self) -> bool { + self.x().is_finite() && self.y().is_finite() + } } impl FloatPointCompatible for [T; 2] { @@ -39,4 +44,11 @@ mod tests { assert_eq!(a0, a1); } + + #[test] + fn test_is_finite() { + assert!([2.0, 5.0].is_finite()); + assert!(![f64::NAN, 5.0].is_finite()); + assert!(![2.0, f64::INFINITY].is_finite()); + } } diff --git a/src/float/number.rs b/src/float/number.rs index 0bee7af..6320397 100644 --- a/src/float/number.rs +++ b/src/float/number.rs @@ -21,6 +21,7 @@ where const ONE: Self; const TWO: Self; const THREE: Self; + const FOUR: Self; const HALF: Self; // Construction. @@ -42,6 +43,7 @@ where fn acos(self) -> Self; fn asin(self) -> Self; fn signum(self) -> Self; + fn is_finite(self) -> bool; // Truncating casts. fn to_i16(self) -> i16; fn to_i32(self) -> i32; @@ -66,6 +68,7 @@ impl FloatNumber for f32 { const ONE: Self = 1.0; const TWO: Self = 2.0; const THREE: Self = 3.0; + const FOUR: Self = 4.0; const HALF: Self = 0.5; // Construction. @@ -150,6 +153,11 @@ impl FloatNumber for f32 { self.signum() } + #[inline(always)] + fn is_finite(self) -> bool { + self.is_finite() + } + // Truncating casts. #[inline(always)] fn to_i16(self) -> i16 { @@ -216,6 +224,7 @@ impl FloatNumber for f64 { const ONE: Self = 1.0; const TWO: Self = 2.0; const THREE: Self = 3.0; + const FOUR: Self = 4.0; const HALF: Self = 0.5; // Construction. #[inline(always)] @@ -299,6 +308,11 @@ impl FloatNumber for f64 { self.signum() } + #[inline(always)] + fn is_finite(self) -> bool { + self.is_finite() + } + // Truncating casts. #[inline(always)] fn to_i16(self) -> i16 {