Featured image of post go小数四舍五入取整

go小数四舍五入取整

掌握Go语言中的小数四舍五入和取整技巧。本文提供了简洁的Go代码示例,展示如何实现小数的四舍五入取整、保留指定小数位数,以及如何在保留小数位数的同时进行四舍五入。通过清晰的数学原理解释和实际代码演示,帮助读者理解并掌握这些常用的数学运算。文章还包含了性能测试,比较了数学计算方法与fmt包格式化方法的性能差异

go的 math 包只提供了简单的小数操作,像常用的四舍五入,保留几位小数这些常用的操作,却没有提供,那只好自己造轮子了。

四舍五入取整

1func Rounding(v float64) int {
2    return int(v + 0.5)
3}

四舍五入取整还是很简单的,直接 +0.5 然后取整就可以了。

保留指定小数位数

1func FloatRetainBit(v float64, bit int) float64 {
2    if bit == 0 {
3        return math.Floor(v)
4    }
5    pow10 := math.Pow10(bit)
6    return math.Floor(v*pow10) / pow10
7}

这个多了一点数学运算,其实就是 * 10 的 多少次方,然后向下取整,再 ÷ 10 的 多少次方就可以了

其中的数学知识:

  • * 10 的 多少次方 就是小数点 移多少位

  • ÷ 10 的 多少次方 就是小数点 移多少位

保留指定小数位数取整

1func FloatRoundingRetainBit(v float64, bit int) float64 {
2    if bit == 0 {
3        return math.Floor(v + 0.5)
4    }
5    pow10 := math.Pow10(bit)
6    return math.Floor(v*pow10+0.5) / pow10
7}

这个也没啥复杂的,就是在保留位数的基础上再加一个四舍五入就好了

测试一下

 1package main
 2
 3import (
 4    "fmt"
 5    "math"
 6    "math/rand"
 7)
 8
 9func main() {
10    f := rand.Float64()
11    fmt.Println(f)
12    fmt.Println(Rounding(f))
13    fmt.Println(FloatRetainBit(f, 4))
14    fmt.Println(FloatRoundingRetainBit(f, 4))
15}
16
17func Rounding(v float64) int {
18    return int(v + 0.5)
19}
20
21func FloatRetainBit(v float64, bit int) float64 {
22    if bit == 0 {
23        return math.Floor(v)
24    }
25    pow10 := math.Pow10(bit)
26    return math.Floor(v*pow10) / pow10
27}
28
29func FloatRoundingRetainBit(v float64, bit int) float64 {
30    if bit == 0 {
31        return math.Floor(v + 0.5)
32    }
33    pow10 := math.Pow10(bit)
34    return math.Floor(v*pow10+0.5) / pow10
35}

看一下运行结果

10.6046602879796196
21
30.6046
40.6047

是符合逻辑的

benchmark

保留指定位数的小数,出了用这种数学计算的方式外,还可以用 fmt 包的函数来做

比如:

1sprintf := fmt.Sprintf("%.4f", f)
2float,  = strconv.ParseFloat(sprintf, 64)

通过占位符 %.4f 来确定小数点后面保留几位

这个方式在性能上比数学计算的要差好多,用benchmark 测试一下

 1package main
 2
 3import (
 4    "fmt"
 5    "math/rand"
 6    "strconv"
 7    "testing"
 8)
 9
10func BenchmarkFloatRetainBit(b *testing.B) {
11    f := rand.Float64()
12    for i := 0; i < b.N; i++ {
13        FloatRetainBit(f, 5)
14    }
15}
16
17func BenchmarkFloatRetainFmt(b *testing.B) {
18    f := rand.Float64()
19    for i := 0; i < b.N; i++ {
20        sprintf := fmt.Sprintf("%.4f", f)
21        _, _ = strconv.ParseFloat(sprintf, 64)
22    }
23}

执行:

1go test -bench . -benchmem

结果:

1goos: windows
2goarch: amd64
3pkg: test2
4cpu: AMD Ryzen 5 3500X 6-Core Processor
5BenchmarkFloatRetainBit-6       310487731                3.865 ns/op           0 B/op          0 allocs/op
6BenchmarkFloatRetainFmt-6        3773605               322.6 ns/op            16 B/op          2 allocs/op
7PASS
8ok      test2   3.179s

可以看到,无论是速度还是内存使用上,都是数学计算更好

发表了56篇文章 · 总计128.44k字
本博客已稳定运行
© QX
使用 Hugo 构建
主题 StackJimmy 设计