src/runtime: improve float/complex hashing

This allows positive and negative zero to hash to the same value,
as required by Go.

This is not perfect, but the best I could do without
revamping all the hash funtions to take a seed.

Fixes #2356
This commit is contained in:
Damian Gryski
2021-12-07 19:03:18 -08:00
committed by Ayke
parent 9734f349a3
commit 1903cf23c9
3 changed files with 77 additions and 3 deletions
+28 -2
View File
@@ -356,6 +356,24 @@ func hashmapStringDelete(m *hashmap, key string) {
//go:linkname valueInterfaceUnsafe reflect.valueInterfaceUnsafe
func valueInterfaceUnsafe(v reflect.Value) interface{}
func hashmapFloat32Hash(ptr unsafe.Pointer) uint32 {
f := *(*uint32)(ptr)
if f == 0x80000000 {
// convert -0 to 0 for hashing
f = 0
}
return hashmapHash(unsafe.Pointer(&f), 4)
}
func hashmapFloat64Hash(ptr unsafe.Pointer) uint32 {
f := *(*uint64)(ptr)
if f == 0x8000000000000000 {
// convert -0 to 0 for hashing
f = 0
}
return hashmapHash(unsafe.Pointer(&f), 8)
}
func hashmapInterfaceHash(itf interface{}) uint32 {
x := reflect.ValueOf(itf)
if x.RawType() == 0 {
@@ -374,13 +392,21 @@ func hashmapInterfaceHash(itf interface{}) uint32 {
return hashmapHash(ptr, x.RawType().Size())
case reflect.Bool, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return hashmapHash(ptr, x.RawType().Size())
case reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128:
case reflect.Float32:
// It should be possible to just has the contents. However, NaN != NaN
// so if you're using lots of NaNs as map keys (you shouldn't) then hash
// time may become exponential. To fix that, it would be better to
// return a random number instead:
// https://research.swtch.com/randhash
return hashmapHash(ptr, x.RawType().Size())
return hashmapFloat32Hash(ptr)
case reflect.Float64:
return hashmapFloat64Hash(ptr)
case reflect.Complex64:
rptr, iptr := ptr, unsafe.Pointer(uintptr(ptr)+4)
return hashmapFloat32Hash(rptr) ^ hashmapFloat32Hash(iptr)
case reflect.Complex128:
rptr, iptr := ptr, unsafe.Pointer(uintptr(ptr)+8)
return hashmapFloat64Hash(rptr) ^ hashmapFloat64Hash(iptr)
case reflect.String:
return hashmapStringHash(x.String())
case reflect.Chan, reflect.Ptr, reflect.UnsafePointer: