From a091a07aa2a70ce45e7c61146b4387630239eae8 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 25 Oct 2023 19:42:00 -0700 Subject: [PATCH 1/3] Add float NaN tests --- test_suite/tests/test_de.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/test_suite/tests/test_de.rs b/test_suite/tests/test_de.rs index 33dc7351b..b7d82ecff 100644 --- a/test_suite/tests/test_de.rs +++ b/test_suite/tests/test_de.rs @@ -11,6 +11,7 @@ #![cfg_attr(feature = "unstable", feature(never_type))] use fnv::FnvHasher; +use serde::de::value::{F32Deserializer, F64Deserializer}; use serde::de::{Deserialize, DeserializeOwned, Deserializer, IntoDeserializer}; use serde_derive::Deserialize; use serde_test::{assert_de_tokens, Configure, Token}; @@ -832,6 +833,26 @@ fn test_f64() { test(1.11, &[Token::F64(1.11)]); } +#[test] +fn test_nan() { + let f32_deserializer = F32Deserializer::::new; + let f64_deserializer = F64Deserializer::::new; + + let pos_f32_nan = f32_deserializer(f32::NAN.copysign(1.0)); + let pos_f64_nan = f64_deserializer(f64::NAN.copysign(1.0)); + assert!(f32::deserialize(pos_f32_nan).unwrap().is_sign_positive()); + assert!(f32::deserialize(pos_f64_nan).unwrap().is_sign_positive()); + assert!(f64::deserialize(pos_f32_nan).unwrap().is_sign_positive()); + assert!(f64::deserialize(pos_f64_nan).unwrap().is_sign_positive()); + + let neg_f32_nan = f32_deserializer(f32::NAN.copysign(-1.0)); + let neg_f64_nan = f64_deserializer(f64::NAN.copysign(-1.0)); + assert!(f32::deserialize(neg_f32_nan).unwrap().is_sign_negative()); + assert!(f32::deserialize(neg_f64_nan).unwrap().is_sign_negative()); + assert!(f64::deserialize(neg_f32_nan).unwrap().is_sign_negative()); + assert!(f64::deserialize(neg_f64_nan).unwrap().is_sign_negative()); +} + #[test] fn test_char() { test('a', &[Token::Char('a')]); From d2fcc346b9f8f8a61d05474b4e95198788c05ed0 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 25 Oct 2023 19:37:47 -0700 Subject: [PATCH 2/3] Ensure f32 deserialized from f64 and vice versa preserve NaN sign --- serde/build.rs | 6 ++++++ serde/src/de/impls.rs | 26 ++++++++++++++++++++++++-- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/serde/build.rs b/serde/build.rs index 2b10c5893..fe5486a7a 100644 --- a/serde/build.rs +++ b/serde/build.rs @@ -27,6 +27,12 @@ fn main() { println!("cargo:rustc-cfg=no_relaxed_trait_bounds"); } + // f32::copysign and f64::copysign stabilized in Rust 1.35. + // https://blog.rust-lang.org/2019/05/23/Rust-1.35.0.html#copy-the-sign-of-a-floating-point-number-onto-another + if minor < 35 { + println!("cargo:rustc-cfg=no_float_copysign"); + } + // Current minimum supported version of serde_derive crate is Rust 1.56. if minor < 56 { println!("cargo:rustc-cfg=no_serde_derive"); diff --git a/serde/src/de/impls.rs b/serde/src/de/impls.rs index fbee1554b..9b8755bd7 100644 --- a/serde/src/de/impls.rs +++ b/serde/src/de/impls.rs @@ -180,6 +180,28 @@ macro_rules! num_as_self { }; } +macro_rules! num_as_copysign_self { + ($ty:ident : $visit:ident) => { + #[inline] + fn $visit(self, v: $ty) -> Result + where + E: Error, + { + #[cfg(no_float_copysign)] + { + Ok(v as Self::Value) + } + + #[cfg(not(no_float_copysign))] + { + // Preserve sign of NaN. The `as` produces a nondeterministic sign. + let sign = if v.is_sign_positive() { 1.0 } else { -1.0 }; + Ok((v as Self::Value).copysign(sign)) + } + } + }; +} + macro_rules! int_to_int { ($ty:ident : $visit:ident) => { #[inline] @@ -351,7 +373,7 @@ impl_deserialize_num! { impl_deserialize_num! { f32, deserialize_f32 num_self!(f32:visit_f32); - num_as_self!(f64:visit_f64); + num_as_copysign_self!(f64:visit_f64); num_as_self!(i8:visit_i8 i16:visit_i16 i32:visit_i32 i64:visit_i64); num_as_self!(u8:visit_u8 u16:visit_u16 u32:visit_u32 u64:visit_u64); } @@ -359,7 +381,7 @@ impl_deserialize_num! { impl_deserialize_num! { f64, deserialize_f64 num_self!(f64:visit_f64); - num_as_self!(f32:visit_f32); + num_as_copysign_self!(f32:visit_f32); num_as_self!(i8:visit_i8 i16:visit_i16 i32:visit_i32 i64:visit_i64); num_as_self!(u8:visit_u8 u16:visit_u16 u32:visit_u32 u64:visit_u64); } From 6ba9c12ff6309114fd22d56a9f4acf32284352f4 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 25 Oct 2023 19:55:12 -0700 Subject: [PATCH 3/3] Float copysign does not exist in libcore yet --- serde/src/de/impls.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/serde/src/de/impls.rs b/serde/src/de/impls.rs index 9b8755bd7..b7e4c549d 100644 --- a/serde/src/de/impls.rs +++ b/serde/src/de/impls.rs @@ -187,12 +187,12 @@ macro_rules! num_as_copysign_self { where E: Error, { - #[cfg(no_float_copysign)] + #[cfg(any(no_float_copysign, not(feature = "std")))] { Ok(v as Self::Value) } - #[cfg(not(no_float_copysign))] + #[cfg(all(not(no_float_copysign), feature = "std"))] { // Preserve sign of NaN. The `as` produces a nondeterministic sign. let sign = if v.is_sign_positive() { 1.0 } else { -1.0 };