What does the following code output?
func main() {
m := make(map[float64]int)
M [1.6] = 1 // 10 lines
M[ math.NaN ()] = 2 // 11 lines
fmt.Println(m[1.6])
fmt.Println(m[1.60000001])
fmt.Println(m[1.60000000000000001])
fmt.Println(m[math.NaN()])
}
Output: 1 0 10
There are two pits in it
First, why
m[1.60000000000000001]
You can get the valueThe second why
m[math.NaN()]
Cannot get value. Let’s analyze them one by one
First
Why?m[1.60000000000000001]
You can get the value,
As usual, indecisive, assembly mechanics~
go tool compile -S main.go | grep main.go:10
Output results (I have removed both the beginning and the end, and only show the key points)
// ....
LEAQ ""..stmp_0(SB), DX
PCDATA $0, $0
MOVQ DX, 16(SP)
CALL runtime.mapassign(SB)
// ....
You can see that when the key of the map isfloat64
In the case of type, thefloat64
Type touin64
Specifically throughmath.Float64bits()
Function completed
// Float64bits returns the IEEE 754 binary representation of f,
// with the sign bit of f and the result in the same bit position,
// and Float64bits(Float64frombits(x)) == x.
func Float64bits(f float64) uint64 { return *(*uint64)(unsafe.Pointer(&f)) }
Let’s test it
fmt.Println(math.Float64bits(1.6))
fmt.Println(math.Float64bits(1.60000000000000001))
output
4609884578576439706
4609884578576439706
As you can see,1.6
and1.60000000000000001
Finally, it is converted to Uint64. The result is the same, so we use them[1.60000000000000001]
You can get the value, of course.
the second
m[math.NaN()]
Why can’t we get the value? Let’s take a look first math.NaN () function details
// NaN returns an IEEE 754 ``not-a-number'' value.
func NaN() float64 { return Float64frombits(uvnan) }
Where uvnan is a constant
uvnan = 0x7FF8000000000001
Nan() calls directlyFloat64frombits
, pass in the write dead value to the Nan type value. Since Nan is parsed from a constant, why is it considered as a different key when inserting a map? In fact, this is determined by the hash function of the type. For the float64 type, its hash function is as follows:
func f64hash(p unsafe.Pointer, h uintptr) uintptr {
f := *(*float64)(p)
switch {
case f == 0:
return c1 * (c0 ^ h) // +0, -0
case f != f:
// any kind of NaN
return c1 * (c0 ^ h ^ uintptr(fastrand()))
default:
return memhash(p, h, 8)
}
}
The second case: F! = f is for Nan, and a random number will be added here.
At this point, it is clear that the key of the map actually stores the hash of the value, and math.NAN () the hash values of return values are different, som[math.NaN()]
There is no value.
Pay attention to my official account and learn GO language together
This work adoptsCC agreementThe author and the link to this article must be indicated in the reprint