很快我发现了另一个极简的算法,它的精度和速度都非常的高,实现还特别精简:
package main import ( "fmt" "github.com/thinkeridea/go-extend/exmath" ) func main() { f := 0.15807659924030304 fmt.Println(float64(int64(f*10000+0.5)) / 10000) // 0.1581 }这并不通用,除非像以下这么包装:
func Round(x, unit float64) float64 { return float64(int64(x*unit+0.5)) / unit }unit 参数和之前的概念不同了,保留一位小数 uint =10,只是整数 uint=1, 想对整数部分进行精度控制 uint=0.01 例如: Round(1555.15807659924030304, 0.01) = 1600,Round(1555.15807659924030304, 1) = 1555,Round(1555.15807659924030304, 10000) = 1555.1581。
这似乎就是终极答案了吧,等等……
终极方案上面的方法够简单,也够高效,但是 api 不太友好,第二个参数不够直观,带了一定的心智负担,其它语言都是传递保留多少位小数,例如 Round(1555.15807659924030304, 0) = 1555,Round(1555.15807659924030304, 2) = 1555.16,Round(1555.15807659924030304, -2) = 1600,这样的交互才符合人性啊。
别急我在 go-extend 开源了 exmath.Round,其算法符合通用语言 Round 实现,且遵循 Round half up 算法要求,其性能方面在 3.50ns/op, 具体可以参看调优exmath.Round算法, 具体代码如下:
//source: https://github.com/thinkeridea/go-extend/blob/main/exmath/round.go package exmath import ( "math" ) // Round 四舍五入,ROUND_HALF_UP 模式实现 // 返回将 val 根据指定精度 precision(十进制小数点后数字的数目)进行四舍五入的结果。precision 也可以是负数或零。 func Round(val float64, precision int) float64 { p := math.Pow10(precision) return math.Floor(val*p+0.5) / p } 总结Round 功能虽简单,但是受到 float 精度影响,仍然有很多人在四处寻找稳定高效的算法,参阅了大多数资料后精简出 exmath.Round 方法,期望对其他开发者有所帮助,至于其精度使用了大量的测试用例,没有超过 float 精度范围时并没有出现精度问题,未知问题等待社区检验,具体测试用例参见 round_test。