Skip to content

Commit 95057ad

Browse files
authored
Bug/582 float nan inf (#588)
exclude infinity from Float and Double wrappers
2 parents 3b250d7 + ff94d26 commit 95057ad

File tree

7 files changed

+91
-82
lines changed

7 files changed

+91
-82
lines changed

nemo-physical/src/datatypes.rs

-3
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,6 @@ pub use double::Double;
1212
/// Module for defining [Float]
1313
pub mod float;
1414
pub use float::Float;
15-
/// Module for defining [FloatIsNaN]
16-
pub(crate) mod float_is_nan;
17-
pub(crate) use float_is_nan::FloatIsNaN;
1815
/// Module for defining [Ring]
1916
pub(crate) mod ring;
2017
pub(crate) use ring::Ring;

nemo-physical/src/datatypes/double.rs

+36-26
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,59 @@
1-
use super::run_length_encodable::FloatingStep;
2-
use super::{FloatIsNaN, FloorToUsize, RunLengthEncodable};
3-
use crate::error::{Error, ReadingError};
4-
use crate::function::definitions::numeric::traits::{CheckedPow, CheckedSquareRoot};
5-
use num::traits::CheckedNeg;
6-
use num::{Bounded, CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, FromPrimitive, One, Zero};
7-
use std::cmp::Ordering;
8-
use std::fmt;
9-
use std::iter::{Product, Sum};
10-
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Rem, Sub, SubAssign};
1+
//! This module defines a wrapper type [Double] for [f64] that excludes NaN and infinity.
2+
3+
use std::{
4+
cmp::Ordering,
5+
fmt,
6+
iter::{Product, Sum},
7+
ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Rem, Sub, SubAssign},
8+
};
9+
10+
use num::{
11+
traits::CheckedNeg, Bounded, CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, FromPrimitive,
12+
One, Zero,
13+
};
14+
15+
use crate::{
16+
error::{Error, ReadingError},
17+
function::definitions::numeric::traits::{CheckedPow, CheckedSquareRoot},
18+
};
19+
20+
use super::{run_length_encodable::FloatingStep, FloorToUsize, RunLengthEncodable};
1121

1222
#[cfg(test)]
1323
use quickcheck::{Arbitrary, Gen};
1424

15-
/// Wrapper for [f64`] that does not allow [`f64::NAN] values.
25+
/// Wrapper for [f64] that excludes [f64::NAN] and infinite values
1626
#[derive(Copy, Clone, Debug, PartialEq, Default)]
1727
pub struct Double(f64);
1828

1929
impl Double {
2030
/// Wraps the given [f64]-`value` as a value over [Double].
2131
///
2232
/// # Errors
23-
/// The given `value` is [f64::NAN].
33+
/// Returns an error if `value` is [f32::NAN] or infinite.
2434
pub fn new(value: f64) -> Result<Self, ReadingError> {
25-
if value.is_nan() {
26-
return Err(FloatIsNaN.into());
35+
if !value.is_finite() {
36+
return Err(ReadingError::InvalidFloat);
2737
}
2838

2939
Ok(Self(value))
3040
}
3141

32-
/// Wraps the given [f64]-`value`, that is a number, as a value over [Double].
42+
/// Wraps the given [f64]-`value` as a value over [Double].
3343
///
3444
/// # Panics
35-
/// The given `value` is [f64::NAN].
45+
/// Panics if `value` is [f64::NAN] or not finite.
3646
pub fn from_number(value: f64) -> Self {
37-
if value.is_nan() {
38-
panic!("The provided value is not a number (NaN)!")
47+
if !value.is_finite() {
48+
panic!("floating point values must be finite")
3949
}
4050

4151
Self(value)
4252
}
4353

4454
/// Computes the absolute value.
4555
pub(crate) fn abs(self) -> Self {
46-
Double::new(self.0.abs()).expect("Taking the absolute value cannot result in NaN")
56+
Double::new(self.0.abs()).expect("operation returns valid float")
4757
}
4858

4959
/// Returns the logarithm of the number with respect to an arbitrary base.
@@ -53,34 +63,34 @@ impl Double {
5363

5464
/// Computes the sine of a number (in radians).
5565
pub(crate) fn sin(self) -> Self {
56-
Double::new(self.0.sin()).expect("Operation does not result in NaN")
66+
Double::new(self.0.sin()).expect("operation returns valid float")
5767
}
5868

5969
/// Computes the cosine of a number (in radians).
6070
pub(crate) fn cos(self) -> Self {
61-
Double::new(self.0.cos()).expect("Operation does not result in NaN")
71+
Double::new(self.0.cos()).expect("operation returns valid float")
6272
}
6373

6474
/// Computes the tangent of a number (in radians).
6575
pub(crate) fn tan(self) -> Self {
66-
Double::new(self.0.tan()).expect("Operation does not result in NaN")
76+
Double::new(self.0.tan()).expect("operation returns valid float")
6777
}
6878

6979
/// Returns the nearest integer to `self`.
7080
/// If a value is half-way between two integers, round away from 0.0.
7181
pub(crate) fn round(self) -> Self {
72-
Double::new(self.0.round()).expect("Operation does not result in NaN")
82+
Double::new(self.0.round()).expect("operation returns valid float")
7383
}
7484

7585
/// Returns the nearest integer to `self`.
7686
/// If a value is half-way between two integers, round away from 0.0.
7787
pub(crate) fn ceil(self) -> Self {
78-
Double::new(self.0.ceil()).expect("Operation does not result in NaN")
88+
Double::new(self.0.ceil()).expect("operation returns valid float")
7989
}
8090

8191
/// Returns the largest integer less than or equal to `self`.
8292
pub(crate) fn floor(self) -> Self {
83-
Double::new(self.0.floor()).expect("Operation does not result in NaN")
93+
Double::new(self.0.floor()).expect("operation returns valid float")
8494
}
8595
}
8696

@@ -294,7 +304,7 @@ impl Bounded for Double {
294304
impl Arbitrary for Double {
295305
fn arbitrary(g: &mut Gen) -> Self {
296306
let mut value = f64::arbitrary(g);
297-
while value.is_nan() {
307+
while !value.is_finite() {
298308
value = f64::arbitrary(g);
299309
}
300310

nemo-physical/src/datatypes/float.rs

+41-32
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,58 @@
1-
use super::run_length_encodable::FloatingStep;
2-
use super::{FloatIsNaN, FloorToUsize, RunLengthEncodable};
3-
use crate::error::Error;
4-
use crate::function::definitions::numeric::traits::{CheckedPow, CheckedSquareRoot};
5-
use num::traits::CheckedNeg;
6-
use num::{Bounded, CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, FromPrimitive, One, Zero};
7-
use std::cmp::Ordering;
8-
use std::fmt;
9-
use std::iter::{Product, Sum};
10-
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Rem, Sub, SubAssign};
1+
//! This module defines a wrapper type [Float] for [f32] that excludes NaN and infinity.
2+
3+
use std::{
4+
cmp::Ordering,
5+
fmt,
6+
iter::{Product, Sum},
7+
ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Rem, Sub, SubAssign},
8+
};
9+
10+
use num::{
11+
traits::CheckedNeg, Bounded, CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, FromPrimitive,
12+
One, Zero,
13+
};
14+
15+
use super::{run_length_encodable::FloatingStep, FloorToUsize, RunLengthEncodable};
16+
use crate::{
17+
error::{Error, ReadingError},
18+
function::definitions::numeric::traits::{CheckedPow, CheckedSquareRoot},
19+
};
1120

1221
#[cfg(test)]
1322
use quickcheck::{Arbitrary, Gen};
1423

15-
/// Wrapper for [f32`] that does not allow [`f32::NAN] values.
24+
/// Wrapper for [f32] that excludes [f32::NAN] and infinite values
1625
#[derive(Copy, Clone, Debug, PartialEq, Default)]
1726
pub struct Float(f32);
1827

1928
impl Float {
2029
/// Wraps the given [f32]-`value` as a value over [Float].
2130
///
2231
/// # Errors
23-
/// The given `value` is [f32::NAN].
24-
pub fn new(value: f32) -> Result<Self, Error> {
25-
if value.is_nan() {
26-
return Err(Error::ReadingError(FloatIsNaN.into()));
32+
/// Returns an error if `value` is [f32::NAN] or infinite.
33+
pub fn new(value: f32) -> Result<Self, ReadingError> {
34+
if !value.is_finite() {
35+
return Err(ReadingError::InvalidFloat);
2736
}
2837

2938
Ok(Float(value))
3039
}
3140

32-
/// Wraps the given [f32]-`value`, that is a number, as a value over [Float].
41+
/// Wraps the given [f32]-`value` as a value over [Float].
3342
///
3443
/// # Panics
35-
/// The given `value` is [f32::NAN].
44+
/// Panics if `value` is [f32::NAN] or not finite.
3645
pub fn from_number(value: f32) -> Self {
37-
if value.is_nan() {
38-
panic!("The provided value is not a number (NaN)!")
46+
if !value.is_finite() {
47+
panic!("floating point values must be finite")
3948
}
4049

4150
Float(value)
4251
}
4352

4453
/// Computes the absolute value.
4554
pub(crate) fn abs(self) -> Self {
46-
Float::new(self.0.abs()).expect("Taking the absolute value cannot result in NaN")
55+
Float::new(self.0.abs()).expect("operation returns valid float")
4756
}
4857

4958
/// Returns the logarithm of the number with respect to an arbitrary base.
@@ -53,33 +62,33 @@ impl Float {
5362

5463
/// Computes the sine of a number (in radians).
5564
pub(crate) fn sin(self) -> Self {
56-
Float::new(self.0.sin()).expect("Operation does not result in NaN")
65+
Float::new(self.0.sin()).expect("operation returns valid float")
5766
}
5867

5968
/// Computes the cosine of a number (in radians).
6069
pub(crate) fn cos(self) -> Self {
61-
Float::new(self.0.cos()).expect("Operation does not result in NaN")
70+
Float::new(self.0.cos()).expect("operation returns valid float")
6271
}
6372

6473
/// Computes the tangent of a number (in radians).
6574
pub(crate) fn tan(self) -> Self {
66-
Float::new(self.0.tan()).expect("Operation does not result in NaN")
75+
Float::new(self.0.tan()).expect("operation returns valid float")
6776
}
6877

6978
/// Returns the nearest integer to `self`.
7079
/// If a value is half-way between two integers, round away from 0.0.
7180
pub(crate) fn round(self) -> Self {
72-
Float::new(self.0.round()).expect("Operation does not result in NaN")
81+
Float::new(self.0.round()).expect("operation returns valid float")
7382
}
7483

7584
/// Returns the smallest integer greater than or equal to `self`.
7685
pub(crate) fn ceil(self) -> Self {
77-
Float::new(self.0.ceil()).expect("Operation does not result in NaN")
86+
Float::new(self.0.ceil()).expect("operation returns valid float")
7887
}
7988

8089
/// Returns the largest integer less than or equal to `self`.
8190
pub(crate) fn floor(self) -> Self {
82-
Float::new(self.0.floor()).expect("Operation does not result in NaN")
91+
Float::new(self.0.floor()).expect("operation returns valid float")
8392
}
8493
}
8594

@@ -95,7 +104,7 @@ impl Ord for Float {
95104
fn cmp(&self, other: &Self) -> Ordering {
96105
self.0
97106
.partial_cmp(&other.0)
98-
.expect("Comparison can only fail on NaN values which have been forbidden in this type")
107+
.expect("comparison can only fail on NaN values which have been forbidden in this type")
99108
}
100109
}
101110

@@ -170,7 +179,7 @@ impl fmt::Display for Float {
170179
}
171180

172181
impl TryFrom<f32> for Float {
173-
type Error = Error;
182+
type Error = ReadingError;
174183

175184
fn try_from(value: f32) -> Result<Self, Self::Error> {
176185
Self::new(value)
@@ -187,9 +196,9 @@ impl TryFrom<usize> for Float {
187196
type Error = Error;
188197

189198
fn try_from(value: usize) -> Result<Self, Self::Error> {
190-
f32::from_usize(value)
191-
.ok_or(Error::UsizeToFloatingPointValue(value))
192-
.and_then(Float::new)
199+
let res = f32::from_usize(value).ok_or(Error::UsizeToFloatingPointValue(value))?;
200+
201+
Ok(Float::new(res)?)
193202
}
194203
}
195204

@@ -293,7 +302,7 @@ impl Bounded for Float {
293302
impl Arbitrary for Float {
294303
fn arbitrary(g: &mut Gen) -> Self {
295304
let mut value = f32::arbitrary(g);
296-
while value.is_nan() {
305+
while !value.is_finite() {
297306
value = f32::arbitrary(g);
298307
}
299308

nemo-physical/src/datatypes/float_is_nan.rs

-18
This file was deleted.

nemo-physical/src/error.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::{convert::Infallible, fmt::Display};
44

55
use thiserror::Error;
66

7-
use crate::{datatypes::FloatIsNaN, resource::Resource};
7+
use crate::resource::Resource;
88

99
/// Trait that can be used by external libraries extending Nemo to communicate a error during reading
1010
pub trait ExternalReadingError: Display + std::fmt::Debug {}
@@ -17,8 +17,8 @@ pub enum ReadingError {
1717
#[error(transparent)]
1818
ExternalError(#[from] Box<dyn std::error::Error>),
1919
/// Error from trying to use a floating point number that is NaN
20-
#[error(transparent)]
21-
FloatIsNaN(#[from] FloatIsNaN),
20+
#[error("floating point values must be finite and not NaN")]
21+
InvalidFloat,
2222
/// Error occurred during parsing of Int values
2323
#[error(transparent)]
2424
ParseInt(#[from] std::num::ParseIntError),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
%! This test case is based on https://github.com/knowsys/nemo/issues/582.
2+
%! A crash was caused by not correctly checking for NaN and infinity when
3+
%! doing computations with floating point numbers.
4+
5+
big(1.7976931348623157E308) .
6+
small(1.0E-32) .
7+
small(0.0) .
8+
9+
result(?a / ?b) :- big(?a), small(?b) .
10+
11+
@export result :- csv {} .

resources/testcases/regression/builtin/float-nan-inf/run/result.csv

Whitespace-only changes.

0 commit comments

Comments
 (0)