From d843064747392a3f7885baa23298ed57193c7638 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 25 Oct 2023 20:37:35 -0700 Subject: [PATCH 1/6] test: Test the sign of NaNs produced by toml macro --- crates/toml/tests/testsuite/macros.rs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/crates/toml/tests/testsuite/macros.rs b/crates/toml/tests/testsuite/macros.rs index 51007051..e002cd91 100644 --- a/crates/toml/tests/testsuite/macros.rs +++ b/crates/toml/tests/testsuite/macros.rs @@ -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] From 2e778c7b5637971ca8d9f34b2d40079b2af33cff Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 25 Oct 2023 20:38:26 -0700 Subject: [PATCH 2/6] fix: Fix sign of NaNs produced by toml macro --- crates/toml/src/macros.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/toml/src/macros.rs b/crates/toml/src/macros.rs index d86cc52f..a2959700 100644 --- a/crates/toml/src/macros.rs +++ b/crates/toml/src/macros.rs @@ -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)) => { From 5ac485e49cf475a7df55f85b50c4b54108aa7b1b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 25 Oct 2023 20:43:22 -0700 Subject: [PATCH 3/6] test: Test sign of NaN created by toml_edit parser --- crates/toml_edit/src/parser/numbers.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/toml_edit/src/parser/numbers.rs b/crates/toml_edit/src/parser/numbers.rs index 6e4757f0..77a54b3b 100644 --- a/crates/toml_edit/src/parser/numbers.rs +++ b/crates/toml_edit/src/parser/numbers.rs @@ -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()); From 09dd4cfc1c8b8c834843bff9a2cdb5fbd2898cd5 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 25 Oct 2023 20:51:54 -0700 Subject: [PATCH 4/6] test: Handle possibility that f64::NAN is negative --- crates/toml_edit/src/parser/numbers.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/toml_edit/src/parser/numbers.rs b/crates/toml_edit/src/parser/numbers.rs index 77a54b3b..5041ff5e 100644 --- a/crates/toml_edit/src/parser/numbers.rs +++ b/crates/toml_edit/src/parser/numbers.rs @@ -377,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), From 151f2e2f424445f105b78ed70c19a3bf0fcf95c4 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 25 Oct 2023 20:55:37 -0700 Subject: [PATCH 5/6] fix: Preserve sign of NaN when serializing f32 --- crates/toml/src/value.rs | 4 +++- crates/toml_edit/src/ser/value.rs | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/crates/toml/src/value.rs b/crates/toml/src/value.rs index e52e5a6a..c8065a6c 100644 --- a/crates/toml/src/value.rs +++ b/crates/toml/src/value.rs @@ -921,7 +921,9 @@ impl ser::Serializer for ValueSerializer { } fn serialize_f32(self, value: f32) -> Result { - 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 { diff --git a/crates/toml_edit/src/ser/value.rs b/crates/toml_edit/src/ser/value.rs index 808bb900..f250d63d 100644 --- a/crates/toml_edit/src/ser/value.rs +++ b/crates/toml_edit/src/ser/value.rs @@ -105,7 +105,9 @@ impl serde::ser::Serializer for ValueSerializer { } fn serialize_f32(self, v: f32) -> Result { - 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 { From 90253a640d23a23afc41b2ea8292a33c65676fa7 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 26 Oct 2023 09:17:43 -0700 Subject: [PATCH 6/6] fix: Deterministically parse nan as a positive NaN --- crates/toml_edit/src/parser/numbers.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/toml_edit/src/parser/numbers.rs b/crates/toml_edit/src/parser/numbers.rs index 5041ff5e..4c77f51c 100644 --- a/crates/toml_edit/src/parser/numbers.rs +++ b/crates/toml_edit/src/parser/numbers.rs @@ -301,7 +301,7 @@ pub(crate) fn inf(input: &mut Input<'_>) -> PResult { const INF: &[u8] = b"inf"; // nan = %x6e.61.6e ; nan pub(crate) fn nan(input: &mut Input<'_>) -> PResult { - tag(NAN).value(f64::NAN).parse_next(input) + tag(NAN).value(f64::NAN.copysign(1.0)).parse_next(input) } const NAN: &[u8] = b"nan";