Skip to content

Commit

Permalink
Merge pull request #637 from dtolnay-contrib/nansign
Browse files Browse the repository at this point in the history
Fix handling of NaN sign throughout parsing and serialization
  • Loading branch information
epage authored Oct 26, 2023
2 parents ed1fcb8 + 90253a6 commit 7f4f543
Show file tree
Hide file tree
Showing 5 changed files with 26 additions and 12 deletions.
6 changes: 3 additions & 3 deletions crates/toml/src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,15 +198,15 @@ macro_rules! toml_internal {
}};

(@value (-nan)) => {
$crate::Value::Float(-::std::f64::NAN)
$crate::Value::Float(::std::f64::NAN.copysign(-1.0))
};

(@value (nan)) => {
$crate::Value::Float(::std::f64::NAN)
$crate::Value::Float(::std::f64::NAN.copysign(1.0))
};

(@value nan) => {
$crate::Value::Float(::std::f64::NAN)
$crate::Value::Float(::std::f64::NAN.copysign(1.0))
};

(@value (-inf)) => {
Expand Down
4 changes: 3 additions & 1 deletion crates/toml/src/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -921,7 +921,9 @@ impl ser::Serializer for ValueSerializer {
}

fn serialize_f32(self, value: f32) -> Result<Value, crate::ser::Error> {
self.serialize_f64(value.into())
// Preserve sign of NaN. The `as` produces a nondeterministic sign.
let sign = if value.is_sign_positive() { 1.0 } else { -1.0 };
self.serialize_f64((value as f64).copysign(sign))
}

fn serialize_f64(self, value: f64) -> Result<Value, crate::ser::Error> {
Expand Down
15 changes: 12 additions & 3 deletions crates/toml/tests/testsuite/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,9 +198,18 @@ fn test_nan() {
sf5 = +nan
sf6 = -nan
};
assert!(actual["sf4"].as_float().unwrap().is_nan());
assert!(actual["sf5"].as_float().unwrap().is_nan());
assert!(actual["sf6"].as_float().unwrap().is_nan());

let sf4 = actual["sf4"].as_float().unwrap();
assert!(sf4.is_nan());
assert!(sf4.is_sign_positive());

let sf5 = actual["sf5"].as_float().unwrap();
assert!(sf5.is_nan());
assert!(sf5.is_sign_positive());

let sf6 = actual["sf6"].as_float().unwrap();
assert!(sf6.is_nan());
assert!(sf6.is_sign_negative());
}

#[test]
Expand Down
9 changes: 5 additions & 4 deletions crates/toml_edit/src/parser/numbers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ pub(crate) fn inf(input: &mut Input<'_>) -> PResult<f64> {
const INF: &[u8] = b"inf";
// nan = %x6e.61.6e ; nan
pub(crate) fn nan(input: &mut Input<'_>) -> PResult<f64> {
tag(NAN).value(f64::NAN).parse_next(input)
tag(NAN).value(f64::NAN.copysign(1.0)).parse_next(input)
}
const NAN: &[u8] = b"nan";

Expand Down Expand Up @@ -353,6 +353,7 @@ mod test {
fn assert_float_eq(actual: f64, expected: f64) {
if expected.is_nan() {
assert!(actual.is_nan());
assert_eq!(expected.is_sign_positive(), actual.is_sign_positive());
} else if expected.is_infinite() {
assert!(actual.is_infinite());
assert_eq!(expected.is_sign_positive(), actual.is_sign_positive());
Expand All @@ -376,9 +377,9 @@ mod test {
("9_224_617.445_991_228_313", 9_224_617.445_991_227),
("-1.7976931348623157e+308", std::f64::MIN),
("1.7976931348623157e+308", std::f64::MAX),
("nan", f64::NAN),
("+nan", f64::NAN),
("-nan", f64::NAN),
("nan", f64::NAN.copysign(1.0)),
("+nan", f64::NAN.copysign(1.0)),
("-nan", f64::NAN.copysign(-1.0)),
("inf", f64::INFINITY),
("+inf", f64::INFINITY),
("-inf", f64::NEG_INFINITY),
Expand Down
4 changes: 3 additions & 1 deletion crates/toml_edit/src/ser/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,9 @@ impl serde::ser::Serializer for ValueSerializer {
}

fn serialize_f32(self, v: f32) -> Result<Self::Ok, Self::Error> {
self.serialize_f64(v as f64)
// Preserve sign of NaN. The `as` produces a nondeterministic sign.
let sign = if v.is_sign_positive() { 1.0 } else { -1.0 };
self.serialize_f64((v as f64).copysign(sign))
}

fn serialize_f64(self, v: f64) -> Result<Self::Ok, Self::Error> {
Expand Down

0 comments on commit 7f4f543

Please sign in to comment.