math.Round()在 Go 中使用四舍五入正值(这里的示例:1.015)四舍五入到小数点后两位:
fmt.Println(math.Round(1.015*100) / 100)
Run Code Online (Sandbox Code Playgroud)
我得到:1.02。没错。
但是当我使用一个函数来做同样的工作时:
func RoundHalfUp(x float64) float64 {
return math.Round(x*100) / 100
}
Run Code Online (Sandbox Code Playgroud)
我得到了1.01。
RoundHalfUp函数有什么问题?
数字常量表示任意精度的精确值并且不会溢出。
实现限制:尽管数字常量在语言中具有任意精度,但编译器可以使用精度有限的内部表示来实现它们。也就是说,每个实施都必须:
- 表示浮点常量,包括复数常量的部分,尾数至少为 256 位,带符号
二进制指数至少为 16 位。- 如果由于精度限制而无法表示浮点或复数常量,则舍入到最接近的可表示常量。
这些要求既适用于文字常量,也适用于计算常量表达式的结果。
常量表达式只能包含常量操作数,并在编译时求值。
常量表达式总是被精确计算;中间值和常量本身可能需要比语言中任何预声明类型支持的精度大得多的精度。
实现限制:编译器在计算无类型浮点或复杂常量表达式时可以使用舍入;请参阅常量部分中的实现限制。这种舍入可能会导致浮点常量表达式在整数上下文中无效,即使在使用无限精度计算时它是整数,反之亦然。
RoundHalfUp像 Go 编译器为math.Round(1.015*100) / 100. 1.015*100是一个无类型浮点常量表达式。使用math/big精度至少为 256 位的包。Go 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"))
}
Run Code Online (Sandbox Code Playgroud)
游乐场:https://play.golang.org/p/uqtYwP4o22B
输出:
1.02
Run Code Online (Sandbox Code Playgroud)
如果我们只使用 53 位精度 ( float64):
xf, _, err := big.ParseFloat(x, 10, 53, big.ToNearestEven)
Run Code Online (Sandbox Code Playgroud)
游乐场:https://play.golang.org/p/ejz-wkuycaU
输出:
1.01
Run Code Online (Sandbox Code Playgroud)