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
Этот коммит содержится в:
родитель
9734f349a3
коммит
1903cf23c9
3 изменённых файлов: 77 добавлений и 3 удалений
|
@ -356,6 +356,24 @@ func hashmapStringDelete(m *hashmap, key string) {
|
||||||
//go:linkname valueInterfaceUnsafe reflect.valueInterfaceUnsafe
|
//go:linkname valueInterfaceUnsafe reflect.valueInterfaceUnsafe
|
||||||
func valueInterfaceUnsafe(v reflect.Value) interface{}
|
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 {
|
func hashmapInterfaceHash(itf interface{}) uint32 {
|
||||||
x := reflect.ValueOf(itf)
|
x := reflect.ValueOf(itf)
|
||||||
if x.RawType() == 0 {
|
if x.RawType() == 0 {
|
||||||
|
@ -374,13 +392,21 @@ func hashmapInterfaceHash(itf interface{}) uint32 {
|
||||||
return hashmapHash(ptr, x.RawType().Size())
|
return hashmapHash(ptr, x.RawType().Size())
|
||||||
case reflect.Bool, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
case reflect.Bool, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||||
return hashmapHash(ptr, x.RawType().Size())
|
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
|
// 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
|
// 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
|
// time may become exponential. To fix that, it would be better to
|
||||||
// return a random number instead:
|
// return a random number instead:
|
||||||
// https://research.swtch.com/randhash
|
// 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:
|
case reflect.String:
|
||||||
return hashmapStringHash(x.String())
|
return hashmapStringHash(x.String())
|
||||||
case reflect.Chan, reflect.Ptr, reflect.UnsafePointer:
|
case reflect.Chan, reflect.Ptr, reflect.UnsafePointer:
|
||||||
|
|
46
testdata/map.go
предоставленный
46
testdata/map.go
предоставленный
|
@ -1,6 +1,8 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import "sort"
|
import (
|
||||||
|
"sort"
|
||||||
|
)
|
||||||
|
|
||||||
var testmap1 = map[string]int{"data": 3}
|
var testmap1 = map[string]int{"data": 3}
|
||||||
var testmap2 = map[string]int{
|
var testmap2 = map[string]int{
|
||||||
|
@ -109,6 +111,48 @@ func main() {
|
||||||
squares = make(map[int]int, 20)
|
squares = make(map[int]int, 20)
|
||||||
testBigMap(squares, 40)
|
testBigMap(squares, 40)
|
||||||
println("tested growing of a map")
|
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) {
|
func readMap(m map[string]int, key string) {
|
||||||
|
|
4
testdata/map.txt
предоставленный
4
testdata/map.txt
предоставленный
|
@ -72,3 +72,7 @@ structMap[{"tau", 3.14}]: 0
|
||||||
structMap[{"tau", 6.28}]: 0
|
structMap[{"tau", 6.28}]: 0
|
||||||
tested preallocated map
|
tested preallocated map
|
||||||
tested growing of a map
|
tested growing of a map
|
||||||
|
2
|
||||||
|
2
|
||||||
|
2
|
||||||
|
2
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче