如何将 float64 数字截断为特定精度?
How can I truncate float64 number to a particular precision?
我想把1.234567
截断成一个三位小数的浮点数,结果不是我想要的
例如:1.234567
=> 1.234
package main
import (
"strconv"
"fmt"
)
func main() {
f := 1.234567
fmt.Println(strconv.FormatFloat(f, 'f', 3, 64)) //1.235
fmt.Printf("%.3f", f) //1.235
}
谁能告诉我如何在 Go 中执行此操作?
您需要在字符串级别或使用 math.Floor
手动截断小数,例如 https://play.golang.org/p/UP2gFx2iFru。
天真的方法(并不总是正确的)
对于截断,我们可以利用 math.Trunc()
丢弃小数位。这不是我们想要的,我们想要保留一些小数位。所以为了达到我们想要的效果,我们可以先将输入乘以 10 的次方,将想要的小数位数移动到 "integer" 部分,并在截断后(调用 math.Trunc()
将丢弃剩余的小数位数),我们可以除以我们在开始时乘以的相同的 10 次方:
f2 := math.Trunc(f*1000) / 1000
将其包装到一个函数中:
func truncateNaive(f float64, unit float64) float64 {
return math.Trunc(f/unit) * unit
}
正在测试:
f := 1.234567
f2 := truncateNaive(f, 0.001)
fmt.Printf("%.10f\n", f2)
输出:
1.2340000000
到目前为止一切顺利,但请注意,我们在 truncateNaive()
内部执行算术运算,这可能会导致不需要的舍入,这可能会改变函数的输出。
例如,如果输入是 0.299999999999999988897769753748434595763683319091796875
(它可以用 float64
值表示,请参阅 proof),输出应该是 0.2999000000
,但它将是别的东西:
f = 0.299999999999999988897769753748434595763683319091796875
f2 = truncateNaive(f, 0.001)
fmt.Printf("%.10f\n", f2)
输出:
0.3000000000
在 Go Playground 上试试这些。
这个错误的输出在大多数情况下可能是不可接受的(除非你从输入非常接近0.3
的角度来看——区别是小于 10-16– 输出为 0.3
...).
使用big.Float
要正确截断所有有效的 float64
值,中间操作必须精确。要实现这一点,使用单个 float64
是不够的。有一些方法可以将输入拆分为 2 个 float64
值并对它们执行操作(因此精度不会丢失)这会更有效,或者我们可以使用更方便的方法,big.Float
可以是任意精度。
这是上面 truncateNaive()
函数的 "transcript" 使用 big.Float
:
func truncate(f float64, unit float64) float64 {
bf := big.NewFloat(0).SetPrec(1000).SetFloat64(f)
bu := big.NewFloat(0).SetPrec(1000).SetFloat64(unit)
bf.Quo(bf, bu)
// Truncate:
i := big.NewInt(0)
bf.Int(i)
bf.SetInt(i)
f, _ = bf.Mul(bf, bu).Float64()
return f
}
正在测试:
f := 1.234567
f2 := truncate(f, 0.001)
fmt.Printf("%.10f\n", f2)
f = 0.299999999999999988897769753748434595763683319091796875
f2 = truncate(f, 0.001)
fmt.Printf("%.10f\n", f2)
输出现在有效(在 Go Playground 上尝试):
1.2340000000
0.2990000000
我想把1.234567
截断成一个三位小数的浮点数,结果不是我想要的
例如:1.234567
=> 1.234
package main
import (
"strconv"
"fmt"
)
func main() {
f := 1.234567
fmt.Println(strconv.FormatFloat(f, 'f', 3, 64)) //1.235
fmt.Printf("%.3f", f) //1.235
}
谁能告诉我如何在 Go 中执行此操作?
您需要在字符串级别或使用 math.Floor
手动截断小数,例如 https://play.golang.org/p/UP2gFx2iFru。
天真的方法(并不总是正确的)
对于截断,我们可以利用 math.Trunc()
丢弃小数位。这不是我们想要的,我们想要保留一些小数位。所以为了达到我们想要的效果,我们可以先将输入乘以 10 的次方,将想要的小数位数移动到 "integer" 部分,并在截断后(调用 math.Trunc()
将丢弃剩余的小数位数),我们可以除以我们在开始时乘以的相同的 10 次方:
f2 := math.Trunc(f*1000) / 1000
将其包装到一个函数中:
func truncateNaive(f float64, unit float64) float64 {
return math.Trunc(f/unit) * unit
}
正在测试:
f := 1.234567
f2 := truncateNaive(f, 0.001)
fmt.Printf("%.10f\n", f2)
输出:
1.2340000000
到目前为止一切顺利,但请注意,我们在 truncateNaive()
内部执行算术运算,这可能会导致不需要的舍入,这可能会改变函数的输出。
例如,如果输入是 0.299999999999999988897769753748434595763683319091796875
(它可以用 float64
值表示,请参阅 proof),输出应该是 0.2999000000
,但它将是别的东西:
f = 0.299999999999999988897769753748434595763683319091796875
f2 = truncateNaive(f, 0.001)
fmt.Printf("%.10f\n", f2)
输出:
0.3000000000
在 Go Playground 上试试这些。
这个错误的输出在大多数情况下可能是不可接受的(除非你从输入非常接近0.3
的角度来看——区别是小于 10-16– 输出为 0.3
...).
使用big.Float
要正确截断所有有效的 float64
值,中间操作必须精确。要实现这一点,使用单个 float64
是不够的。有一些方法可以将输入拆分为 2 个 float64
值并对它们执行操作(因此精度不会丢失)这会更有效,或者我们可以使用更方便的方法,big.Float
可以是任意精度。
这是上面 truncateNaive()
函数的 "transcript" 使用 big.Float
:
func truncate(f float64, unit float64) float64 {
bf := big.NewFloat(0).SetPrec(1000).SetFloat64(f)
bu := big.NewFloat(0).SetPrec(1000).SetFloat64(unit)
bf.Quo(bf, bu)
// Truncate:
i := big.NewInt(0)
bf.Int(i)
bf.SetInt(i)
f, _ = bf.Mul(bf, bu).Float64()
return f
}
正在测试:
f := 1.234567
f2 := truncate(f, 0.001)
fmt.Printf("%.10f\n", f2)
f = 0.299999999999999988897769753748434595763683319091796875
f2 = truncate(f, 0.001)
fmt.Printf("%.10f\n", f2)
输出现在有效(在 Go Playground 上尝试):
1.2340000000
0.2990000000