Skip to content

Commit 43a510a

Browse files
committed
new(module): hashes
1 parent 0d24e47 commit 43a510a

1 file changed

Lines changed: 56 additions & 0 deletions

File tree

src/float_utils/hashes.nim

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
2+
import std/hashes; export Hash
3+
from ./isX import isfinite, isinf, isnan
4+
from std/math import frexp
5+
6+
const PyHASH_BITS* = when sizeof(pointer) >= 8: 61 else: 31
7+
const PyHASH_MODULUS* = (1 shl PyHASH_BITS) - 1
8+
9+
const PyHASH_INF* = 314159
10+
11+
type
12+
Py_uhash_t = uint
13+
static: assert sizeof(Py_uhash_t) == sizeof(Hash)
14+
15+
16+
proc Py_HashDouble*(v: float): Hash =
17+
## `_Py_HashDouble`
18+
if not isfinite(v):
19+
if isinf(v):
20+
return if v > 0.0: PyHASH_INF else: -PyHASH_INF
21+
else:
22+
raise newException(ValueError, "cannot hash NaN")
23+
24+
var (m, e) = frexp(v)
25+
26+
var sign = true
27+
if m < 0.0:
28+
sign = false
29+
m = -m
30+
31+
var x: Py_uhash_t = 0
32+
var y: Py_uhash_t
33+
34+
while m != 0.0:
35+
x = ((x shl 28) and PyHASH_MODULUS) or (x shr (PyHASH_BITS - 28))
36+
m *= 268435456.0 # 2**28
37+
e -= 28
38+
y = Py_uhash_t(m)
39+
m -= float(y)
40+
x += y
41+
if x >= PyHASH_MODULUS:
42+
x -= PyHASH_MODULUS
43+
44+
# adjust for the exponent; first reduce it modulo PyHASH_BITS
45+
e = if e >= 0:
46+
e mod PyHASH_BITS
47+
else:
48+
PyHASH_BITS - 1 - ((-1 - e) mod PyHASH_BITS)
49+
x = ((x shl e) and PyHASH_MODULUS) or (x shr (PyHASH_BITS - e))
50+
51+
x = x * Py_uhash_t(sign)
52+
if x == Py_uhash_t.high:
53+
x = cast[Py_uhash_t](-2)
54+
return Hash x
55+
56+
proc hash*(v: float): Hash = Py_HashDouble(v)

0 commit comments

Comments
 (0)