From 68f9c65bb1191b789918292a2ad25c0f8b227421 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Haudebourg?= Date: Fri, 5 Nov 2021 12:51:02 +0100 Subject: [PATCH] Use the same hash for +0 and -0. --- src/number.rs | 9 ++++++++- tests/test.rs | 17 +++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/number.rs b/src/number.rs index ad59ae6dd..a5771dbbd 100644 --- a/src/number.rs +++ b/src/number.rs @@ -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 this case we use the +0 hash so that hash(+0) == hash(-0). + 0.0f64.to_bits().hash(h); + } else { + f.to_bits().hash(h); + } } } } diff --git a/tests/test.rs b/tests/test.rs index c8df23dba..25198f5d3 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -2284,3 +2284,20 @@ 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::("0.0").unwrap(); + let k2 = serde_json::from_str::("-0.0").unwrap(); + assert_eq!(k1, k2); + assert_eq!(hash(k1), hash(k2)); +}