Skip to content

Commit d2fcc34

Browse files
committed
Ensure f32 deserialized from f64 and vice versa preserve NaN sign
1 parent a091a07 commit d2fcc34

2 files changed

Lines changed: 30 additions & 2 deletions

File tree

serde/build.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ fn main() {
2727
println!("cargo:rustc-cfg=no_relaxed_trait_bounds");
2828
}
2929

30+
// f32::copysign and f64::copysign stabilized in Rust 1.35.
31+
// https://blog.rust-lang.org/2019/05/23/Rust-1.35.0.html#copy-the-sign-of-a-floating-point-number-onto-another
32+
if minor < 35 {
33+
println!("cargo:rustc-cfg=no_float_copysign");
34+
}
35+
3036
// Current minimum supported version of serde_derive crate is Rust 1.56.
3137
if minor < 56 {
3238
println!("cargo:rustc-cfg=no_serde_derive");

serde/src/de/impls.rs

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,28 @@ macro_rules! num_as_self {
180180
};
181181
}
182182

183+
macro_rules! num_as_copysign_self {
184+
($ty:ident : $visit:ident) => {
185+
#[inline]
186+
fn $visit<E>(self, v: $ty) -> Result<Self::Value, E>
187+
where
188+
E: Error,
189+
{
190+
#[cfg(no_float_copysign)]
191+
{
192+
Ok(v as Self::Value)
193+
}
194+
195+
#[cfg(not(no_float_copysign))]
196+
{
197+
// Preserve sign of NaN. The `as` produces a nondeterministic sign.
198+
let sign = if v.is_sign_positive() { 1.0 } else { -1.0 };
199+
Ok((v as Self::Value).copysign(sign))
200+
}
201+
}
202+
};
203+
}
204+
183205
macro_rules! int_to_int {
184206
($ty:ident : $visit:ident) => {
185207
#[inline]
@@ -351,15 +373,15 @@ impl_deserialize_num! {
351373
impl_deserialize_num! {
352374
f32, deserialize_f32
353375
num_self!(f32:visit_f32);
354-
num_as_self!(f64:visit_f64);
376+
num_as_copysign_self!(f64:visit_f64);
355377
num_as_self!(i8:visit_i8 i16:visit_i16 i32:visit_i32 i64:visit_i64);
356378
num_as_self!(u8:visit_u8 u16:visit_u16 u32:visit_u32 u64:visit_u64);
357379
}
358380

359381
impl_deserialize_num! {
360382
f64, deserialize_f64
361383
num_self!(f64:visit_f64);
362-
num_as_self!(f32:visit_f32);
384+
num_as_copysign_self!(f32:visit_f32);
363385
num_as_self!(i8:visit_i8 i16:visit_i16 i32:visit_i32 i64:visit_i64);
364386
num_as_self!(u8:visit_u8 u16:visit_u16 u32:visit_u32 u64:visit_u64);
365387
}

0 commit comments

Comments
 (0)