Skip to content

Commit

Permalink
stdlib: better Hash for Float64/Float32
Browse files Browse the repository at this point in the history
fixes #90
  • Loading branch information
soc committed Dec 21, 2023
1 parent 8970cfe commit 9e39418
Show file tree
Hide file tree
Showing 3 changed files with 16 additions and 8 deletions.
12 changes: 10 additions & 2 deletions dora/stdlib/primitives.dora
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,10 @@ impl Float32 {
@pub @internal fun isNan(): Bool;
@pub @internal fun sqrt(): Float32;

@pub fun hash(): Int32 = self.asInt32();
@pub fun hash(): Int32 =
// discard the sign, such that 0.0 and -0.0 hash the same, at the cost of all positive numbers
// and their negative counterparts hashing the same, but keeping the computation branch-free.
self.asInt32() & 0x7FFFFFFFi32;

// should be lets, not funs
@pub @static fun bits(): Int32 = 32i32;
Expand Down Expand Up @@ -405,7 +408,12 @@ impl Float64 {
@pub @internal fun isNan(): Bool;
@pub @internal fun sqrt(): Float64;

@pub fun hash(): Int32 = self.asInt64().toInt32();
@pub fun hash(): Int32 = {
// discard the sign, such that 0.0 and -0.0 hash the same, at the cost of all positive numbers
// and their negative counterparts hashing the same, but keeping the computation branch-free.
let hash = self.asInt64() & 7FFFFFFFFFFFFFFF;
(hash ^ (hash.shiftRight(32))).asInt32
}

// should be lets, not funs
@pub @static fun bits(): Int32 = 64i32;
Expand Down
8 changes: 4 additions & 4 deletions tests/stdlib/hashmap-contains-zero.dora
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ fun float64(): Unit {
assert(map.size() == 1);
assert(map.contains(0.0));
assert(map.get(0.0).getOrPanic() == "a");
assert(map.contains(-0.0)); // FIXME: this only works by coincidence, because the hash function sucks
assert(map.get(-0.0).getOrPanic() == "a"); // FIXME: this only works by coincidence, because the hash function sucks
assert(map.contains(-0.0));
assert(map.get(-0.0).getOrPanic() == "a");
}

fun float32(): Unit {
Expand All @@ -19,6 +19,6 @@ fun float32(): Unit {
assert(map.size() == 1);
assert(map.contains(0.0f32));
assert(map.get(0.0f32).getOrPanic() == "a");
assert(map.contains(-0.0f32).not()); // FIXME: should be true instead
// assert(map.get(-0.0f32).getOrPanic() == "a"); // FIXME: should be true instead
assert(map.contains(-0.0f32));
assert(map.get(-0.0f32).getOrPanic() == "a");
}
4 changes: 2 additions & 2 deletions tests/stdlib/hashset-contains-zero.dora
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ fun float64(): Unit {

assert(set.size() == 1);
assert(set.contains(0.0));
assert(set.contains(-0.0)); // FIXME: this only works by coincidence, because the hash function sucks
assert(set.contains(-0.0));
}

fun float32(): Unit {
let set = std::HashSet[Float32]::new(0.0f32);

assert(set.size() == 1);
assert(set.contains(0.0f32));
assert(set.contains(-0.0f32).not()); // FIXME: should be true instead
assert(set.contains(-0.0f32));
}

0 comments on commit 9e39418

Please sign in to comment.