Skip to content

Commit

Permalink
Use the same hash for +0 and -0.
Browse files Browse the repository at this point in the history
  • Loading branch information
Timothée Haudebourg committed Nov 5, 2021
1 parent d12fbb9 commit f53ae31
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 1 deletion.
9 changes: 8 additions & 1 deletion src/number.rs
Expand Up @@ -55,7 +55,14 @@ impl core::hash::Hash for N {
N::NegInt(i) => i.hash(h),
N::Float(f) => {
// Using `f64::to_bits` here is fine since any float values are never `NaN`.
f.to_bits().hash(h);
if *f == 0.0f64 {
// The IEEE 754 standard has two representations for zero, +0 and -0,
// such that +0 == -0.
// In both cases we use the +0 hash so that hash(+0) == hash(-0).
0.0f64.to_bits().hash(h);
} else {
f.to_bits().hash(h);
}
}
}
}
Expand Down
16 changes: 16 additions & 0 deletions tests/test.rs
Expand Up @@ -2284,3 +2284,19 @@ fn test_value_into_deserializer() {
let outer = Outer::deserialize(map.into_deserializer()).unwrap();
assert_eq!(outer.inner.string, "Hello World");
}

#[test]
fn hash_positive_and_negative_zero() {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};

fn hash(obj: impl Hash) -> u64 {
let mut hasher = DefaultHasher::new();
obj.hash(&mut hasher);
hasher.finish()
}

let k1 = serde_json::from_str::<Number>("0.0").unwrap();
let k2 = serde_json::from_str::<Number>("-0.0").unwrap();
assert!(k1 != k2 || hash(k1) == hash(k2));
}

0 comments on commit f53ae31

Please sign in to comment.