【发布时间】:2022-10-23 16:52:13
【问题描述】:
我有一个输入可能是一个非常大或非常小的浮点数,需要将其转换为big.Int,但由于某种原因,存在一些精度损失。
我知道这应该发生在非常小的数字上,但是为什么会发生在大数字上,以及如何避免呢?
【问题讨论】:
-
通过尝试将值存储在
float64中,您甚至在使用big.Float之前已经失去了精度
标签: go
我有一个输入可能是一个非常大或非常小的浮点数,需要将其转换为big.Int,但由于某种原因,存在一些精度损失。
我知道这应该发生在非常小的数字上,但是为什么会发生在大数字上,以及如何避免呢?
【问题讨论】:
float64 中,您甚至在使用 big.Float 之前已经失去了精度
标签: go
全部正整数最多 9007199254740992 可以用 float64 表示,而不会损失任何精度。任何更高的值,您都会面临精度损失的风险,这在您的情况下正在发生。
给出一个基本的想法为什么..
假设我们正在发明一个非常紧凑的方案来使用以下公式表示浮点数:
m.mm * 10^+-e
.. 在哪里:
有了这个,我们可以确定可以表示的值范围:
所以这是一个相当不错的数字范围。
我们可以毫无困难地表示相当多的正整数,例如
1 = 1.00 * 10^0
2 = 2.00 * 10^0
3 = 3.00 * 10^0
⋮
10 = 1.00 * 10^1
11 = 1.10 * 10^1
12 = 1.20 * 10^1
⋮
100 = 1.00 * 10^2
101 = 1.01 * 10^2
102 = 1.01 * 10^2
⋮
999 = 9.99 * 10^2
当我们超过9.99 * 10^2 时,问题就开始了。表示 1000 不是问题:
1000 = 1.00 * 10^3
但是如何表示 1001?下一个可能的值是
1.01 * 10^3 = 1010
这是 +9 的精度损失,所以我们必须解决 1.00 * 10^3 的精度损失 -1。
上面的内容本质上是使用 float64 的结果,除了基数 2 和 52 位尾数。设置所有 52 位,然后加一,值为:
1.0 * 2^53 = 9007199254740992
因此,直到该值的所有正整数都可以在没有精度损失的情况下表示。高于此的整数可能导致精度损失 - 这在很大程度上取决于价值。
现在,您的 Go 代码中引用的值:
var x float64 = 827273999999999954
无法将此值表示为 float64。
package main
import (
"fmt"
)
func main() {
var x float64 = 827273999999999954
fmt.Printf("%f
", x)
}
产量..
827274000000000000.000000
因此,在初始化 x 时,精度基本上会丢失。但是什么时候发生呢?如果我们跑..
$ go build -o tmp
$ go tool objdump tmp
并搜索TEXT main.main(SB),我们可以找到指令:
main.go:10 0x108b654 48b840d5cba322f6a643 MOVQ $0x43a6f622a3cbd540, AX
所以0x43a6f622a3cbd540 被设置为 AX - 这是我们的 float64 值。
package main
import (
"fmt"
"math"
)
func main() {
fmt.Printf("float: %f
", math.Float64frombits(0x43a6f622a3cbd540))
}
印刷
float: 827274000000000000.000000
所以精度在编译时基本上已经丢失(这是有道理的)。所以在big.NewFloat(x).Int(nil) 的代码行中,作为x 传递的值是827274000000000000.000000
如何避免呢?
使用您提供的代码,没有办法。
如果您能够将值表示为整数..
package main import ( "fmt" "math/big" ) func main() { var x uint64 = 827273999999999954 bf := (&big.Float{}).SetUint64(x) fmt.Println(bf) }产量
8.27273999999999954e+17这是您期望的值。或者通过字符串:
package main import ( "fmt" "math/big" ) func main() { var x string = "827273999999999954" bf, ok := (&big.Float{}).SetString(x) if !ok { panic("failed to set string") } fmt.Println(bf) }
【讨论】: