go小数四舍五入取整

202 阅读2分钟

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

四舍五入取整

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

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

保留指定小数位数

func FloatRetainBit(v float64, bit int) float64 {
    if bit == 0 {
        return math.Floor(v)
    }
    pow10 := math.Pow10(bit)
    return math.Floor(v*pow10) / pow10
}

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

其中的数学知识:

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

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

保留指定小数位数取整

func FloatRoundingRetainBit(v float64, bit int) float64 {
    if bit == 0 {
        return math.Floor(v + 0.5)
    }
    pow10 := math.Pow10(bit)
    return math.Floor(v*pow10+0.5) / pow10
}

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

测试一下

package main

import (
    "fmt"
    "math"
    "math/rand"
)

func main() {
    f := rand.Float64()
    fmt.Println(f)
    fmt.Println(Rounding(f))
    fmt.Println(FloatRetainBit(f, 4))
    fmt.Println(FloatRoundingRetainBit(f, 4))
}

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

func FloatRetainBit(v float64, bit int) float64 {
    if bit == 0 {
        return math.Floor(v)
    }
    pow10 := math.Pow10(bit)
    return math.Floor(v*pow10) / pow10
}

func FloatRoundingRetainBit(v float64, bit int) float64 {
    if bit == 0 {
        return math.Floor(v + 0.5)
    }
    pow10 := math.Pow10(bit)
    return math.Floor(v*pow10+0.5) / pow10
}

看一下运行结果

0.6046602879796196
1
0.6046
0.6047

是符合逻辑的

benchmark

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

比如:

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

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

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

package main

import (
    "fmt"
    "math/rand"
    "strconv"
    "testing"
)

func BenchmarkFloatRetainBit(b *testing.B) {
    f := rand.Float64()
    for i := 0; i < b.N; i++ {
        FloatRetainBit(f, 5)
    }
}

func BenchmarkFloatRetainFmt(b *testing.B) {
    f := rand.Float64()
    for i := 0; i < b.N; i++ {
        sprintf := fmt.Sprintf("%.4f", f)
        _, _ = strconv.ParseFloat(sprintf, 64)
    }
}

执行:

go test -bench . -benchmem

结果:

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

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