66
package main

import (
    "fmt"
    "strconv"
    )

func main() {
    k := 10/3.0
    i := fmt.Sprintf("%.2f", k)
    f,_ := strconv.ParseFloat(i, 2)
    fmt.Println(f)
}

我必须编写上面的程序来将 go float64 变量的精度降低到 2。在这种情况下,我同时使用了 strconv 和 fmt。是否有其他一些合乎逻辑的方法可以做到这一点?

4

13 回答 13

80

以下代码应该适用于数量相对较少且输入精度较低的许多简单用例。但是,它可能不适用于某些用例,因为数字超出了 float64 数字的范围,以及 IEEE-754 舍入错误(其他语言也有这个问题)。

如果您关心使用更大的数字或需要更高的精度,以下代码可能无法满足您的需求,您应该使用帮助库(例如https://github.com/shopspring/decimal)。

我从别处拿起了一个单线轮函数,并且还制作了依赖于 round() 的 toFixed():

func round(num float64) int {
    return int(num + math.Copysign(0.5, num))
}

func toFixed(num float64, precision int) float64 {
    output := math.Pow(10, float64(precision))
    return float64(round(num * output)) / output
}

用法:

fmt.Println(toFixed(1.2345678, 0))  // 1
fmt.Println(toFixed(1.2345678, 1))  // 1.2
fmt.Println(toFixed(1.2345678, 2))  // 1.23
fmt.Println(toFixed(1.2345678, 3))  // 1.235 (rounded up)
于 2015-04-22T01:58:21.367 回答
72

你不需要任何额外的代码......它很简单

import (
    "fmt"
)

func main() {
    k := 10 / 3.0
    fmt.Printf("%.2f", k)
}

测试代码

于 2013-08-24T12:27:14.763 回答
27

我真的不明白这一点,但你可以在没有 strconv 的情况下做这样的事情:

package main

import (
    "fmt"
)

func main() {
    untruncated := 10 / 3.0
    truncated := float64(int(untruncated * 100)) / 100
    fmt.Println(untruncated, truncated)
}
于 2013-08-22T21:23:11.677 回答
15

最简单的解决方案是数字截断(假设i是浮点数,并且您需要 2 个小数点的精度):

float64(int(i * 100)) / 100

例如:

i := 123456.789
x := float64(int(i * 100)) / 100
// x = 123456.78

谨防!

如果您正在处理大数字(可以跨越最大值边界的数字),您应该知道上述可能导致严重的浮点精度问题

i := float64(1<<63) // 9223372036854775808.0
fmt.Println(i, float64(int64(i * 10)) / 10)

印刷:9.223372036854776e+18 -9.223372036854776e+17

也可以看看:

  1. 32 位浮点数如何工作
  2. 64 位浮点数如何工作
  3. golangmath数值范围常量
  4. 戈朗math/big
于 2018-08-17T03:52:20.387 回答
10

没有人提到使用math/big. 与原始问题相关的结果与接受的答案相同,但如果您使用的浮点数需要一定程度的精度($money$),那么您应该使用big.Float.

根据原始问题:

package main

import (
    "math/big"
    "fmt"
)

func main() {
    // original question
    k := 10 / 3.0
    fmt.Println(big.NewFloat(k).Text('f', 2))
}

不幸的是,您可以看到它.Text不使用定义的舍入模式(否则这个答案可能更有用),而是似乎总是向零舍入:

j := 0.045
fmt.Println(big.NewFloat(j).SetMode(big.AwayFromZero).Text('f', 2)

// out -> 0.04

尽管如此,将浮点数存储为big.Float.

于 2016-06-07T03:28:43.293 回答
6

threeve的回答让我在 GitHub 上找到了这个问题,其中提出了一个基于math/big舍入值的解决方案 - 这样就可以正确使用舍入方法:

package main

import (
    "fmt"
    "math/big"
)

func main() {
    f := new(big.Float).SetMode(big.ToNearestEven).SetFloat64(10/3.0)
    // Round to 2 digits using formatting.
    f.SetPrec(8)
    fmt.Printf("%.2f\n", f)
}

在threeve的示例中也尊重舍入模式:

j := 0.045

f := new(big.Float).SetMode(big.AwayFromZero).SetFloat64(j)
// Round to 2 digits using formatting.
f.SetPrec(8)
fmt.Printf("%.2f\n", f)

->正确产生0.05

此外,Go 1.10 已发布并添加了一个math.Round()功能,请参阅 icza 的出色回答:Golang Round to Nearest 0.05

package main

import (
    "fmt"
    "math"
)

func main() {

    fmt.Println(Round(10/3.0, 0.01))

}

func Round(x, unit float64) float64 {
    return math.Round(x/unit) * unit
}

但是,不应该float用于存储货币价值。(请参阅:为什么不使用 Double 或 Float 来表示货币?)解决此问题的一种方法是使用实​​现decimal类似https://github.com/ericlagergren/decimalhttps://github.com/shopspring/decimal的库

于 2018-03-08T14:10:07.610 回答
5

不检查大浮点数的函数

// Rounds like 12.3416 -> 12.35
func RoundUp(val float64, precision int) float64 {
    return math.Ceil(val*(math.Pow10(precision))) / math.Pow10(precision)
}

// Rounds like 12.3496 -> 12.34
func RoundDown(val float64, precision int) float64 {
    return math.Floor(val*(math.Pow10(precision))) / math.Pow10(precision)
}

// Rounds to nearest like 12.3456 -> 12.35
func Round(val float64, precision int) float64 {
    return math.Round(val*(math.Pow10(precision))) / math.Pow10(precision)
}
于 2020-11-02T13:19:31.690 回答
3

小心经度和纬度浮动,因为截断和四舍五入会产生完全不同的结果……因为它可能会使您处于纬度边界的错误一侧。

于 2018-05-18T08:16:53.820 回答
2

这是一个小解决方法,如何使用类型转换为 int 来回舍入浮点数:

package main

import (
    "fmt"
)

func main() {
    k := 10 / 3.0
    k = float64(int(k*100)) / 100
    fmt.Println(k)  // output 3.33
}

https://play.golang.org/p/yg2QYcZ-2u

于 2017-07-15T00:04:35.073 回答
0
func truncate(val float64, precision int) float64 {
      return math.Round(val * precision) / precision
}
于 2022-02-10T14:42:09.837 回答
0

因此,经过多次寻找,多次来到这个线程,我终于找到了一个非常简单的解决方案,它可以让您非常简单地控制浮点数的精度,而无需任何奇怪的数学!

package main

import (
    "fmt"
    "github.com/shopspring/decimal"
)

func main() {
    k := 101.3874927181298723478
    p := 5

    v := decimal.NewFromFloat(k).Round(int32(p))
    fmt.Println(v)
}
// 101.38749

来源:https ://godoc.org/github.com/shopspring/decimal#Decimal.Round

虽然我喜欢一些简单的方法,比如"%.3f\n", k选项,但这些方法会产生一个字符串,然后我必须使用另一个我不想执行的 strconv 命令将其转换回浮点数。

于 2020-07-31T15:00:39.300 回答
0
func FloatPrecision(num float64, precision int) float64 {
    p := math.Pow10(precision)
    value := float64(int(num*p)) / p
    return value
}
于 2020-04-17T17:51:17.770 回答
-3

从@creack 修改

package main

import (
    "fmt"
)

func main() {

    //untruncated := 10/3.0
    untruncated := 4.565
    tmp := int(untruncated*100)
    last := int(untruncated*1000)-tmp*10
    if last>=5{
        tmp += 1
    }
    truncated := float64(tmp)/100

    fmt.Println(untruncated, truncated)
}
于 2013-08-25T10:29:11.857 回答