Skip to content

Commit

Permalink
Merge #163
Browse files Browse the repository at this point in the history
163: Implement inf, -inf and NaN handling r=torkleyy a=artemshein

FIxes #159

Co-authored-by: Artem Shein <[email protected]>
  • Loading branch information
bors[bot] and artemshein committed May 26, 2019
2 parents 8003fe4 + 8c72672 commit 4f36c26
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 7 deletions.
6 changes: 6 additions & 0 deletions src/de/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,12 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> {
return visitor.visit_none();
} else if self.bytes.consume("()") {
return visitor.visit_unit();
} else if self.bytes.consume_ident("inf") {
return visitor.visit_f64(std::f64::INFINITY);
} else if self.bytes.consume_ident("-inf") {
return visitor.visit_f64(std::f64::NEG_INFINITY);
} else if self.bytes.consume_ident("NaN") {
return visitor.visit_f64(std::f64::NAN);
}

// `identifier` does not change state if it fails
Expand Down
12 changes: 12 additions & 0 deletions src/de/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,18 @@ mod tests {
);
}

#[test]
fn test_floats() {
assert_eq!(
eval("(inf, -inf, NaN)"),
Value::Seq(vec![
Value::Number(Number::new(std::f64::INFINITY)),
Value::Number(Number::new(std::f64::NEG_INFINITY)),
Value::Number(Number::new(std::f64::NAN)),
]),
);
}

#[test]
fn test_complex() {
assert_eq!(
Expand Down
7 changes: 7 additions & 0 deletions src/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,13 @@ impl<'a> Bytes<'a> {
where
T: FromStr,
{
for literal in &["inf", "-inf", "NaN"] {
if self.consume_ident(literal) {
return FromStr::from_str(literal)
.map_err(|_| unreachable!()); // must not fail
}
}

let num_bytes = self.next_bytes_contained_in(FLOAT_CHARS);

let s = unsafe { from_utf8_unchecked(&self.bytes[0..num_bytes]) };
Expand Down
48 changes: 41 additions & 7 deletions src/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,11 @@ use crate::de::{Error as RonError, Result};

/// A wrapper for `f64` which guarantees that the inner value
/// is finite and thus implements `Eq`, `Hash` and `Ord`.
#[derive(Copy, Clone, Debug, PartialOrd, PartialEq)]
#[derive(Copy, Clone, Debug)]
pub struct Number(f64);

impl Number {
/// Panics if `v` is not a real number
/// (infinity, NaN, ..).
pub fn new(v: f64) -> Self {
if !v.is_finite() {
panic!("Tried to create Number with a NaN / infinity");
}

Number(v)
}

Expand All @@ -36,6 +30,22 @@ impl Number {
}
}

/// Partial equality comparison
/// In order to be able to use `Number` as a mapping key, NaN floating values wrapped in `Number`
/// are equals to each other. It is not the case for underlying `f64` values itself.
impl PartialEq for Number {

fn eq(&self, other: &Self) -> bool {
if self.0.is_nan() && other.0.is_nan() {
return true;
}
return self.0 == other.0;
}
}

/// Equality comparison
/// In order to be able to use `Number` as a mapping key, NaN floating values wrapped in `Number`
/// are equals to each other. It is not the case for underlying `f64` values itself.
impl Eq for Number {}

impl Hash for Number {
Expand All @@ -44,6 +54,30 @@ impl Hash for Number {
}
}

/// Partial ordering comparison
/// In order to be able to use `Number` as a mapping key, NaN floating values wrapped in `Number`
/// are equals to each other and are less then any other floating value.
/// It is not the case for underlying `f64` values itself.
/// ```
/// use ron::value::Number;
/// assert!(Number::new(std::f64::NAN) < Number::new(std::f64::NEG_INFINITY));
/// assert_eq!(Number::new(std::f64::NAN), Number::new(std::f64::NAN));
/// ```
impl PartialOrd for Number {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
match (self.0.is_nan(), other.0.is_nan()) {
(true, true) => Some(Ordering::Equal),
(true, false) => Some(Ordering::Less),
(false, true) => Some(Ordering::Greater),
_ => self.0.partial_cmp(&other.0)
}
}
}

/// Ordering comparison
/// In order to be able to use `Number` as a mapping key, NaN floating values wrapped in `Number`
/// are equals to each other and are less then any other floating value.
/// It is not the case for underlying `f64` values itself. See the `PartialEq` implementation.
impl Ord for Number {
fn cmp(&self, other: &Self) -> Ordering {
self.partial_cmp(other).expect("Bug: Contract violation")
Expand Down
8 changes: 8 additions & 0 deletions tests/floats.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
use ron::de::from_str;

#[test]
fn test_inf_and_nan() {
assert_eq!(from_str("inf"), Ok(std::f64::INFINITY));
assert_eq!(from_str("-inf"), Ok(std::f64::NEG_INFINITY));
assert_eq!(from_str::<f64>("NaN").map(|n| n.is_nan()), Ok(true))
}

0 comments on commit 4f36c26

Please sign in to comment.