Go踩坑笔记(十九) 
                                                    
                        
                    
                    
  
                    
                    下面代码输出什么?
func main() {
    m := make(map[float64]int)
    m[1.6] = 1 // 10行
    m[math.NaN()] = 2 // 11行
    fmt.Println(m[1.6])
    fmt.Println(m[1.60000001])
    fmt.Println(m[1.60000000000000001])
    fmt.Println(m[math.NaN()])
}输出:1 0 1 0
这里面有个两个坑点:
第一个 为什么m[1.60000000000000001]可以取到值
第二个 为什么m[math.NaN()]取不到值。我们一个一个来分析
首先第一个
为什么 m[1.60000000000000001] 可以获取到值,
老样子,犹豫不决,汇编力学~
go tool compile -S main.go | grep main.go:10输出结果(首尾我都去掉了,只展示比较关键的地方)
// ....
LEAQ    ""..stmp_0(SB), DX
PCDATA  $0, $0
MOVQ    DX, 16(SP)
CALL    runtime.mapassign(SB)
// ....可以看到当map的key是float64类型的时候,会先将float64类型转成 uin64
具体是通过 math.Float64bits() 函数完成的
// 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)) }我们测试一下
fmt.Println(math.Float64bits(1.6))
fmt.Println(math.Float64bits(1.60000000000000001))输出
4609884578576439706
4609884578576439706可以看到,1.6 和 1.60000000000000001 最后转成 uint64 类型结果是一样的,所以我们使用 m[1.60000000000000001] 也就理所当然的可以获取到值了。
第二个
m[math.NaN()] 为什么取不到值,我们先看下 math.NaN() 函数具体内容
// NaN returns an IEEE 754 ``not-a-number'' value.
func NaN() float64 { return Float64frombits(uvnan) }其中 uvnan 是一个常量
uvnan = 0x7FF8000000000001NAN() 直接调用 Float64frombits,传入写死的值得到 NAN 型值。既然,NAN 是从一个常量解析得来的,为什么插入 map 时,会被认为是不同的 key?其实这是由类型的哈希函数决定的,对于float64类型,它的哈希函数如下:
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)
    }
}第二个case: f != f 就是针对 NAN 的,这里会再加一个随机数。
到这里就清楚了,因为 map 的 key 最后其实存的是值的hash,而math.NAN() 返回值的 hash 值都是不同的,所以 m[math.NaN()] 是取不到值的 。
关注我公众号一起学习GO语言,1~18的坑都在我公众号上,有时间我再搬运过来吧

本作品采用《CC 协议》,转载必须注明作者和本文链接
 
           pibigstar 的个人博客
 pibigstar 的个人博客
         
             
           
           关于 LearnKu
                关于 LearnKu
               
                     
                     
                     粤公网安备 44030502004330号
 粤公网安备 44030502004330号 
 
推荐文章: