在将浮点数转换为整数时,如何避免浮点数误差。例如,以下代码输出:0.5499999999999972
,但实际期望输出结果是 0.55
。
package main
import "fmt"
func main() {
x := 100.55
fmt.Println(x - float64(int(x)))
}
Output:
0.5499999999999972
在将浮点数转换为整数时,如何避免浮点数误差。例如,以下代码输出:0.5499999999999972
,但实际期望输出结果是 0.55
。
package main
import "fmt"
func main() {
x := 100.55
fmt.Println(x - float64(int(x)))
}
Output:
0.5499999999999972
100.55
是一个十进制数(以十进制基数表示)。在十进制中,100.55
是一个有限的数,确切地说就是 100.55
。100.55
无法用有限的二进制数字表示:在二进制表示中,100.55
是一个无限的数字(与不可能用有限的十进制数字表示1/3
相同,它是一个无尽的序列:0.333333333....
)。float64
类型,这是一个有限的二进制表示。一个float64
值使用64位内存描述该数字,其中53位用于表示数字,11位用于指数。x := 100.55
这是一个简短变量声明,将创建一个名为x
的新变量,并从右侧表达式推断其类型,该表达式是一个浮点文字,因此按照Go规范,x
的类型将为float64
。 浮点文字必须进行“转换”,以便使用64位表示(根据IEEE-754
指定的规则)。由于100.55
需要无限位才能在二进制基数中精确表示,在仅使用64位(53位用于数字)的情况下,结果将不会(也无法)完全等于100.55
(而是IEEE-754格式中最接近它的64位二进制数),即:
x := 100.55
fmt.Printf("%.50f\n", x)
100.54999999999999715782905695959925651550292968750000
所以你开始的数值并不是 100.55
。
你从中减去 100
(float64(int(x))
将会恰好是 100.0
):
x = x - float64(int(x))
fmt.Printf("%.50f\n", x)
0.54999999999999715782905695959925651550292968750000
你能做些什么呢?其实没什么。你期望的结果(0.55
)在二进制表示中也是无限的,因此你不能在float64
类型的变量中获得精确的0.55
数字。
你可以像平常一样使用这个数字,但在打印输出时,将它舍入到你选择的小数位数。最简单的方法是使用fmt.Printf()
,并使用格式化字符串和%f
动词指定精度:
fmt.Printf("%.2f\n", x)
结果:
0.55
另一个选项是避免使用浮点数。例如,如果你要表示美元金额,你可以将所有的值 "乘以" 100
并将金额表示为分(1 美元 * 100 = 100 分),这是一个整数。只有当你需要将结果打印为美元时,你才能打印出类似下面这样的内容
cents := 10055
fmt.Printf("%d.%d $", cents/100, cents%100)
输出:
100.55 $
100.55 - 100
。我在这两种情况下得到的结果都是0.5499999999999972
。你是否熟悉浮点数算术中的陷阱? - Amit Kumar Guptafmt.Printf
有什么问题吗? - Volker