在Go中使用math.Round()
将正整数(例如:1.015)舍入为小数点后两位(小数点后两位):
fmt.Println(math.Round(1.015*100) / 100)
我得到了:1.02
。没错。
但是当我使用一个函数来完成相同的工作时:
func RoundHalfUp(x float64) float64 {
return math.Round(x*100) / 100
}
我得到了1.01
。
RoundHalfUp
函数怎么了?
答案 0 :(得分:1)
The Go Programming Language Specification
数值常数表示任意精度的精确值,并且 不溢出。
实施限制:尽管数字常量具有任意性 语言的精确度,编译器可以使用 内部表示,精度有限。也就是说,每个 实施必须:
- 表示浮点常量,包括复数常量的部分,尾数至少为256位,并且带符号
至少16位的二进制指数。- 如果由于精度限制而无法表示浮点或复数常量,则将其舍入到最接近的可表示常量。
这些要求既适用于文字常量也适用于结果 常数表达式的计算。
常量表达式只能包含常量操作数,并且 在编译时评估。
常量表达式总是精确计算的;中间值 常量本身可能需要很高的精度 大于该语言中任何预先声明的类型所支持的大小。
实现限制:编译器可能在 计算无类型的浮点数或复杂的常量表达式;看到 常量部分中的实现限制。这个 舍入可能导致浮点常量表达式无效 在整数上下文中,即使在计算时将是整数 使用无限精度,反之亦然。
像Go编译器为RoundHalfUp
一样实现math.Round(1.015*100) / 100
函数。 1.015*100
是一个无类型的浮点常量表达式。使用math/big
包,至少要有256位精度。 float64
(IEEE-754 64位浮点数)具有53位精度。
例如,具有256位精度(常量表达式),
package main
import (
"fmt"
"math"
"math/big"
)
func RoundHalfUp(x string) float64 {
// math.Round(x*100) / 100
xf, _, err := big.ParseFloat(x, 10, 256, big.ToNearestEven)
if err != nil {
panic(err)
}
xf100, _ := new(big.Float).Mul(xf, big.NewFloat(100)).Float64()
return math.Round(xf100) / float64(100)
}
func main() {
fmt.Println(RoundHalfUp("1.015"))
}
游乐场:https://play.golang.org/p/uqtYwP4o22B
输出:
1.02
如果我们仅使用53位精度(float64
):
xf, _, err := big.ParseFloat(x, 10, 53, big.ToNearestEven)
游乐场:https://play.golang.org/p/ejz-wkuycaU
输出:
1.01