# Notes of go stepping on the pit (19)

Time：2020-12-4

#### 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 value

The 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 is`float64`In the case of type, the`float64`Type to`uin64`

Specifically through`math.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`and`1.60000000000000001`Finally, it is converted to Uint64. The result is the same, so we use the`m[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 directly`Float64frombits`, 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, so`m[math.NaN()]`There is no value.

Pay attention to my official account and learn GO language together 