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
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Загрузка…
	
	Создание таблицы
		
		Сослаться в новой задаче