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
Этот коммит содержится в:
Damian Gryski 2021-12-07 19:03:18 -08:00 коммит произвёл Ayke
родитель 9734f349a3
коммит 1903cf23c9
3 изменённых файлов: 77 добавлений и 3 удалений

Просмотреть файл

@ -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:

46
testdata/map.go предоставленный
Просмотреть файл

@ -1,6 +1,8 @@
package main
import "sort"
import (
"sort"
)
var testmap1 = map[string]int{"data": 3}
var testmap2 = map[string]int{
@ -109,6 +111,48 @@ func main() {
squares = make(map[int]int, 20)
testBigMap(squares, 40)
println("tested growing of a map")
floatcmplx()
}
func floatcmplx() {
var zero float64
var negz float64 = -zero
// test that zero and negative zero hash to the same thing
m := make(map[float64]int)
m[zero]++
m[negz]++
println(m[negz])
cmap := make(map[complex128]int)
var c complex128
c = complex(zero, zero)
cmap[c]++
c = complex(negz, negz)
cmap[c]++
c = complex(0, 0)
println(cmap[c])
c = complex(1, negz)
cmap[c]++
c = complex(1, zero)
cmap[c]++
println(cmap[c])
c = complex(negz, 2)
cmap[c]++
c = complex(zero, 2)
cmap[c]++
println(cmap[c])
}
func readMap(m map[string]int, key string) {

4
testdata/map.txt предоставленный
Просмотреть файл

@ -72,3 +72,7 @@ structMap[{"tau", 3.14}]: 0
structMap[{"tau", 6.28}]: 0
tested preallocated map
tested growing of a map
2
2
2
2