Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "i_float"
version = "3.0.0"
version = "4.0.0"
authors = ["Nail Sharipov <nailxsharipov@gmail.com>"]
edition = "2024"
description = "This fixed float math library provides an efficient and deterministic solution for arithmetic and geometric operations."
Expand Down
34 changes: 24 additions & 10 deletions src/adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,17 +115,17 @@ impl<P: FloatPointCompatible, I: IntNumber> FloatPointAdapter<P, I> {
rect: FloatRect<P::Scalar>,
scale: P::Scalar,
) -> Result<Self, FloatPointAdapterScaleError> {
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)
}

Expand Down Expand Up @@ -157,15 +157,14 @@ impl<P: FloatPointCompatible, I: IntNumber> FloatPointAdapter<P, I> {
}

#[inline]
fn validate_scale(scale: P::Scalar) -> Result<f64, FloatPointAdapterScaleError> {
let scale_f64 = scale.to_f64();
if !scale_f64.is_finite() {
fn validate_scale(scale: P::Scalar) -> Result<P::Scalar, FloatPointAdapterScaleError> {
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)]
Expand Down Expand Up @@ -248,7 +247,7 @@ impl<P: FloatPointCompatible, I: IntNumber> FloatPointAdapter<P, I> {

#[inline(always)]
pub fn try_float_to_int(&self, point: &P) -> Result<IntPoint<I>, FloatPointAdapterRangeError> {
if !self.rect.contains(point) {
if !point.is_finite() || !self.rect.contains(point) {
return Err(FloatPointAdapterRangeError::PointOutOfRange);
}

Expand Down Expand Up @@ -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 =
Expand Down
12 changes: 12 additions & 0 deletions src/float/compatible.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<T: FloatNumber> FloatPointCompatible for [T; 2] {
Expand Down Expand Up @@ -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());
}
}
14 changes: 14 additions & 0 deletions src/float/number.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ where
const ONE: Self;
const TWO: Self;
const THREE: Self;
const FOUR: Self;
const HALF: Self;

// Construction.
Expand All @@ -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;
Expand All @@ -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.
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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)]
Expand Down Expand Up @@ -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 {
Expand Down