Skip to content

Commit

Permalink
avoid $x string allocation; fix another bug that could lead to collis…
Browse files Browse the repository at this point in the history
…ions for small flaots
  • Loading branch information
timotheecour committed Jul 18, 2019
1 parent 0302ad4 commit 93b46c4
Showing 1 changed file with 16 additions and 5 deletions.
21 changes: 16 additions & 5 deletions lib/pure/hashes.nim
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,20 @@ proc hash*(x: string): Hash

proc hash*[T: SomeNumber | Ordinal | char](x: T): Hash {.inline.} =
## Efficient hashing of numbers, ordinals (eg enum), char.
# fix #11764
when preferStringHash(T):
when preferStringHash(T): # fix #11764
when T is SomeFloat:
# needed otherwise finite precision can cause hash duplicate
let x = cast[BiggestInt](x)
hash($x)
# 0.0 vs -0.0 should map to same hash to avoid weird behavior.
# the only non nan value that can cause clash is 0 according to
# https://stackoverflow.com/questions/31087915/are-there-denormalized-floats-that-evaluate-to-the-same-value-apart-from-0-0
# bugfix: the previous code was using `x = x + 1.0` (presumably for
# handling negative 0), however this doesn't work well for small inputs
# because `x+1.0` can become 0 with floating point accuracy, which
# leads to hash collisions.
# Note: this hit this bug: #11775:
# `let x = if x == 0.0: 0.0 else: x`
var x = x
if x == 0: x = 0
hashData(cast[pointer](unsafeAddr x), T.sizeof)
else:
# more efficient for small types
ord(x)
Expand Down Expand Up @@ -99,6 +107,9 @@ proc `!$`*(h: Hash): Hash {.inline.} =

proc hashData*(data: pointer, size: int): Hash =
## Hashes an array of bytes of size `size`.
# this should probably be merged/refactored with
# `proc hash*[A](aBuf: openArray[A], sPos, ePos: int): Hash =` to avoid
# using 2 different algorithms
var h: Hash = 0
when defined(js):
var p: cstring
Expand Down

0 comments on commit 93b46c4

Please sign in to comment.