mirror of
https://github.com/RustPython/RustPython.git
synced 2026-06-02 19:39:49 +09:00
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:
2
Lib/test/test_float.py
vendored
2
Lib/test/test_float.py
vendored
@@ -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))
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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())))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user