Merge pull request #3951 from jopemachine/float-nan-hash

Fix `float` and `complex`'s incorrect hash value when using with `nan`
This commit is contained in:
Jeong YunWon
2022-07-27 15:15:35 +09:00
committed by GitHub
4 changed files with 34 additions and 24 deletions

View File

@@ -582,8 +582,6 @@ class GeneralFloatCases(unittest.TestCase):
self.assertEqual(hash(float('inf')), sys.hash_info.inf)
self.assertEqual(hash(float('-inf')), -sys.hash_info.inf)
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_hash_nan(self):
value = float('nan')
self.assertEqual(hash(value), object.__hash__(value))

View File

@@ -1,11 +1,7 @@
use num_bigint::BigInt;
use num_complex::Complex64;
use num_traits::ToPrimitive;
use siphasher::sip::SipHasher24;
use std::{
hash::{BuildHasher, Hash, Hasher},
num::Wrapping,
};
use std::hash::{BuildHasher, Hash, Hasher};
pub type PyHash = i64;
pub type PyUHash = u64;
@@ -94,17 +90,14 @@ impl HashSecret {
}
}
pub fn hash_float(value: f64) -> PyHash {
#[inline]
pub fn hash_float(value: f64) -> Option<PyHash> {
// cpython _Py_HashDouble
if !value.is_finite() {
return if value.is_infinite() {
if value > 0.0 {
INF
} else {
-INF
}
Some(if value > 0.0 { INF } else { -INF })
} else {
NAN
None
};
}
@@ -136,14 +129,7 @@ pub fn hash_float(value: f64) -> PyHash {
};
x = ((x << e) & MODULUS) | x >> (BITS32 - e);
fix_sentinel(x as PyHash * value.signum() as PyHash)
}
pub fn hash_complex(value: &Complex64) -> PyHash {
let re_hash = hash_float(value.re);
let im_hash = hash_float(value.im);
let Wrapping(ret) = Wrapping(re_hash) + Wrapping(im_hash) * Wrapping(IMAG);
fix_sentinel(ret)
Some(fix_sentinel(x as PyHash * value.signum() as PyHash))
}
pub fn hash_iter_unordered<'a, T: 'a, I, F, E>(iter: I, hashf: F) -> Result<PyHash, E>
@@ -192,3 +178,19 @@ pub fn lcg_urandom(mut x: u32, buf: &mut [u8]) {
*b = ((x >> 16) & 0xff) as u8;
}
}
#[inline]
pub fn hash_object_id_raw(p: usize) -> PyHash {
// TODO: Use commented logic when below issue resolved.
// Ref: https://github.com/RustPython/RustPython/pull/3951#issuecomment-1193108966
/* bottom 3 or 4 bits are likely to be 0; rotate y by 4 to avoid
excessive hash collisions for dicts and sets */
// p.rotate_right(4) as PyHash
p as PyHash
}
#[inline]
pub fn hash_object_id(p: usize) -> PyHash {
fix_sentinel(hash_object_id_raw(p))
}

View File

@@ -16,6 +16,7 @@ use crate::{
use num_complex::Complex64;
use num_traits::Zero;
use rustpython_common::{float_ops, hash};
use std::num::Wrapping;
/// Create a complex number from a real part and an optional imaginary part.
///
@@ -417,7 +418,16 @@ impl Comparable for PyComplex {
impl Hashable for PyComplex {
#[inline]
fn hash(zelf: &crate::Py<Self>, _vm: &VirtualMachine) -> PyResult<hash::PyHash> {
Ok(hash::hash_complex(&zelf.value))
let value = zelf.value;
let re_hash =
hash::hash_float(value.re).unwrap_or_else(|| hash::hash_object_id(zelf.get_id()));
let im_hash =
hash::hash_float(value.im).unwrap_or_else(|| hash::hash_object_id(zelf.get_id()));
let Wrapping(ret) = Wrapping(re_hash) + Wrapping(im_hash) * Wrapping(hash::IMAG);
Ok(hash::fix_sentinel(ret))
}
}

View File

@@ -539,7 +539,7 @@ impl Comparable for PyFloat {
impl Hashable for PyFloat {
#[inline]
fn hash(zelf: &crate::Py<Self>, _vm: &VirtualMachine) -> PyResult<hash::PyHash> {
Ok(hash::hash_float(zelf.to_f64()))
Ok(hash::hash_float(zelf.to_f64()).unwrap_or_else(|| hash::hash_object_id(zelf.get_id())))
}
}