From 936f501b55f77736748ceb5afb0bf7d956813096 Mon Sep 17 00:00:00 2001 From: Michael Spector Date: Sun, 7 Mar 2021 11:44:11 +0200 Subject: [PATCH 1/3] Fixed #66: equal numbers issue different hashes --- src/dec128.rs | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/src/dec128.rs b/src/dec128.rs index 4d6faab0..8737208c 100644 --- a/src/dec128.rs +++ b/src/dec128.rs @@ -83,7 +83,15 @@ impl Encodable for d128 { impl Hash for d128 { fn hash(&self, state: &mut H) { - self.bytes.hash(state); + d128::with_context(|ctx| unsafe { + let mut num_self: MaybeUninit = MaybeUninit::uninit(); + decimal128ToNumber(self, num_self.as_mut_ptr()); + let mut num_self = num_self.assume_init(); + decNumberTrim(&mut num_self); + let mut trimmed_self: d128 = d128::default(); + decimal128FromNumber(&mut trimmed_self, &num_self, ctx); + trimmed_self.bytes.hash(state); + }); } } @@ -993,6 +1001,7 @@ extern "C" { rhs: *const decNumber, ctx: *mut Context) -> *mut decNumber; + fn decNumberTrim(src: *mut decNumber) -> *mut decNumber; } #[cfg(test)] @@ -1001,6 +1010,7 @@ mod tests { use super::*; #[cfg(any(feature = "ord_subset", feature = "serde"))] use std::collections::BTreeMap; + use std::collections::hash_map::DefaultHasher; #[cfg(feature = "ord_subset")] use ord_subset; @@ -1146,4 +1156,28 @@ mod tests { assert_eq!(d128!(10), decimals.into_iter().sum()); } + + #[test] + fn test_hash() { + let d1 = d128::from_str("0.100").unwrap(); + let d2 = d128::from_str("0.1").unwrap(); + assert_eq!(d1, d2); + let mut hasher = DefaultHasher::new(); + d1.hash(&mut hasher); + let h1 = hasher.finish(); + let mut hasher = DefaultHasher::new(); + d2.hash(&mut hasher); + let h2 = hasher.finish(); + assert_eq!(h1, h2); + + let d1 = d128!(0.123); + let d2 = d128!(0.234); + let mut hasher = DefaultHasher::new(); + d1.hash(&mut hasher); + let h1 = hasher.finish(); + let mut hasher = DefaultHasher::new(); + d2.hash(&mut hasher); + let h2 = hasher.finish(); + assert_ne!(h1, h2); + } } From e923bd2e10f79b8632438c762986b60b029f8fe1 Mon Sep 17 00:00:00 2001 From: Michael Spector Date: Mon, 8 Mar 2021 09:04:14 +0200 Subject: [PATCH 2/3] Extract trim() function --- src/dec128.rs | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/src/dec128.rs b/src/dec128.rs index 8737208c..3dc14397 100644 --- a/src/dec128.rs +++ b/src/dec128.rs @@ -83,15 +83,8 @@ impl Encodable for d128 { impl Hash for d128 { fn hash(&self, state: &mut H) { - d128::with_context(|ctx| unsafe { - let mut num_self: MaybeUninit = MaybeUninit::uninit(); - decimal128ToNumber(self, num_self.as_mut_ptr()); - let mut num_self = num_self.assume_init(); - decNumberTrim(&mut num_self); - let mut trimmed_self: d128 = d128::default(); - decimal128FromNumber(&mut trimmed_self, &num_self, ctx); - trimmed_self.bytes.hash(state); - }); + let trimmed_self = self.trim(); + trimmed_self.bytes.hash(state) } } @@ -745,6 +738,19 @@ impl d128 { d128::with_context(|ctx| unsafe { *decQuadScaleB(&mut self, &self, other.as_ref(), ctx) }) } + /// Returns self with removed insignificant trailing zeroes. + pub fn trim(&self) -> d128 { + d128::with_context(|ctx| unsafe { + let mut num_self: MaybeUninit = MaybeUninit::uninit(); + decimal128ToNumber(self, num_self.as_mut_ptr()); + let mut num_self = num_self.assume_init(); + decNumberTrim(&mut num_self); + let mut trimmed_self: d128 = d128::default(); + decimal128FromNumber(&mut trimmed_self, &num_self, ctx); + trimmed_self + }) + } + // Comparisons. /// Compares `self` and `other` numerically and returns the result. The result may be –1, 0, 1, @@ -1157,6 +1163,13 @@ mod tests { assert_eq!(d128!(10), decimals.into_iter().sum()); } + #[test] + fn test_trim() { + let d = d128::from_str("0.100").unwrap(); + assert_eq!("0.100", d.to_string()); + assert_eq!("0.1", d.trim().to_string()); + } + #[test] fn test_hash() { let d1 = d128::from_str("0.100").unwrap(); From 9875bee97496bc7292323f873fe57b11a6cf38f5 Mon Sep 17 00:00:00 2001 From: Michael Spector Date: Mon, 8 Mar 2021 23:04:48 +0200 Subject: [PATCH 3/3] Use reduce() instead of trim() --- src/dec128.rs | 25 ++----------------------- 1 file changed, 2 insertions(+), 23 deletions(-) diff --git a/src/dec128.rs b/src/dec128.rs index 3dc14397..cdc66bed 100644 --- a/src/dec128.rs +++ b/src/dec128.rs @@ -83,8 +83,8 @@ impl Encodable for d128 { impl Hash for d128 { fn hash(&self, state: &mut H) { - let trimmed_self = self.trim(); - trimmed_self.bytes.hash(state) + let reduced_self = self.reduce(); + reduced_self.bytes.hash(state) } } @@ -738,19 +738,6 @@ impl d128 { d128::with_context(|ctx| unsafe { *decQuadScaleB(&mut self, &self, other.as_ref(), ctx) }) } - /// Returns self with removed insignificant trailing zeroes. - pub fn trim(&self) -> d128 { - d128::with_context(|ctx| unsafe { - let mut num_self: MaybeUninit = MaybeUninit::uninit(); - decimal128ToNumber(self, num_self.as_mut_ptr()); - let mut num_self = num_self.assume_init(); - decNumberTrim(&mut num_self); - let mut trimmed_self: d128 = d128::default(); - decimal128FromNumber(&mut trimmed_self, &num_self, ctx); - trimmed_self - }) - } - // Comparisons. /// Compares `self` and `other` numerically and returns the result. The result may be –1, 0, 1, @@ -1007,7 +994,6 @@ extern "C" { rhs: *const decNumber, ctx: *mut Context) -> *mut decNumber; - fn decNumberTrim(src: *mut decNumber) -> *mut decNumber; } #[cfg(test)] @@ -1163,13 +1149,6 @@ mod tests { assert_eq!(d128!(10), decimals.into_iter().sum()); } - #[test] - fn test_trim() { - let d = d128::from_str("0.100").unwrap(); - assert_eq!("0.100", d.to_string()); - assert_eq!("0.1", d.trim().to_string()); - } - #[test] fn test_hash() { let d1 = d128::from_str("0.100").unwrap();