为什么这两个大的 Float 值不相等?

Why are these 2 big Float values not equal?

在过去的两周里,我一直在研究浮点数的行为和功能,特别是 GO 中的大浮点数。我遇到了很多行为并自己找到了答案。但是,我仍然找不到一个答案。

https://play.golang.org/p/-y0oeb2Jisv

value1 := big.NewFloat(137216723432.8234782347)
value2 := big.NewFloat(71371.92602458)
for i := 0; i < 300; i++ {
    value1.Sub(value1, value2)
}

value3 := big.NewFloat(137216723432.8234782347)
value4 := big.NewFloat(71371.92602458)
for i := 0; i < 300; i++ {
    result := big.NewFloat(0).Sub(value3,value4)
    value3.Set(result)
}

encodedValue1, _ := value1.GobEncode()
encodedValue3, _ := value3.GobEncode()

if value1 == value3 {
    fmt.Println("values are equal" , value1 , value3)
} else {
    fmt.Println("values are not equal", value1 ,value3)
}

fmt.Println("difference is here:\n", encodedValue1,"\n", encodedValue3)

为什么这2个操作的结果不相等?据我了解,这与 precision/accuracy/rounding 模式有关。

谢谢!

value1value3 是指针,所以 value1 == value3 比较的是这些指针,而不是指向的值。有可能 2 个指向的对象相等但它们的地址不相等。

比较big.Float values (or *big.Float), use the Float.Cmp()方法。 returns 0 如果 2 个值(它们代表的数字)相等。

if value1.Cmp(value3) == 0 {
    fmt.Println("values are equal", value1, value3)
} else {
    fmt.Println("values are not equal", value1, value3)
}

通过此更改输出将是(在 Go Playground 上尝试):

values are equal 1.3719531185501585e+11 1.3719531185501585e+11
difference is here:
 [1 2 0 0 0 53 0 0 0 37 255 139 210 151 120 32 120 0] 
 [1 10 0 0 0 53 0 0 0 37 255 139 210 151 120 32 120 0]

所以代表的数字是相等的。

Float.GobEncode()返回的序列化二进制形式不一样,但这并不意味着表示的数字不相等。正如其文档所述:

GobEncode implements the gob.GobEncoder interface. The Float value and all its attributes (precision, rounding mode, accuracy) are marshaled.

输出不同,因为 big.Float 的内部结构不同(在本例中为准确度)。在这种情况下,即使您可以比较指向的对象,它们也不会相同,但表示的数字是相同的。同样,始终使用提供的方法来比较复杂对象,当然不是地址。

此示例中的差异来自存储的精度字段:

fmt.Println(value1.Acc())
fmt.Println(value3.Acc())

哪些输出(在 Go Playground 上尝试):

Below
Exact

Float.Acc()返回的准确度是“最近一次操作产生的x的准确度”。由于在 value1value3 上执行的最后一个操作不相同(value1.Sub()value3.Set()),因此精度字段不一定相同(在本例中它们是相同的)不同)。并且由于精度属性也包含在Gob序列化形式中,这就是为什么它们的序列化形式不同。