Go:使用乘法将float64转换为int

3

我想把一个float64类型的数字,比如说1.003,转换成整数1003。我的实现方法是将float64乘以1000,然后将其转换为int

package main

import "fmt"


func main() {
  var f float64 = 1.003
  fmt.Println(int(f * 1000))
}

但是当我运行这段代码时,得到的结果是1002而不是1003。这是因为Go自动将1.003存储为1.002999...在变量中。那么在Golang中进行这种操作的正确方法是什么?

3个回答

8

Go规范:类型转换:

数值类型之间的转换

将浮点数转换为整数时,小数部分会被舍去(向零舍入)。

所以,当将浮点数转换为整数时,只有整数部分会保留。

如果您只想避免由于使用有限位数表示而导致的错误,在将其转换为int之前,只需将0.5添加到该数字即可。不需要外部库或函数调用(来自标准库)。

由于float -> int转换不是四舍五入,而是保留整数部分,因此这将为您提供所需的结果。同时考虑可能的较小和较大表示:

1002.9999 + 0.5 = 1003.4999;     integer part: 1003
1003.0001 + 0.5 = 1003.5001;     integer part: 1003

那么只需简单地编写如下内容:
var f float64 = 1.003
fmt.Println(int(f * 1000 + 0.5))

将这段代码封装成一个函数,示例如下:
func toint(f float64) int {
    return int(f + 0.5)
}

// Using it:
fmt.Println(toint(f * 1000))

请在Go Playground上尝试它们。

注意:

在处理负数时请小心!例如,如果您有一个值为-1.003的数,则可能希望结果为-1003。但是,如果您将其加上0.5

-1002.9999 + 0.5 = -1002.4999;     integer part: -1002
-1003.0001 + 0.5 = -1002.5001;     integer part: -1002

如果你有负数,你需要执行以下操作之一:

  • 减去0.5而不是加上它
  • 或者加上0.5但从结果中减去1

将其合并到我们的辅助函数中:

func toint(f float64) int {
    if f < 0 {
        return int(f - 0.5)
    }
    return int(f + 0.5)
}

3
正如Will所述,这与浮点数在各种平台上的表示方式有关。基本上,您需要四舍五入浮点数,而不是让默认截断行为发生。可能没有标准库函数可用于此,因为可能会有很多可能的行为,而且实现起来很简单。
如果您知道您总是会遇到描述的那种错误,即您略低于(1299.999999)所需值(1300.00000),则可以使用math库的Ceil函数:
f := 1.29999
n := math.Ceil(f*1000)

但是如果你有不同类型的浮点误差并且想要更通用的排序行为呢?使用数学库的Modf函数通过小数点将你的浮点值分开:

f := 1.29999

f1,f2 := math.Modf(f*1000)
n := int(f1) // n = 1299   
if f2 > .5 { 
    n++
}
fmt.Println(n)

您可以在playground上运行稍微更通用的版本的代码。

0

这可能是大多数编程语言中浮点数的一个普遍问题,尽管有些语言的实现方式不同。我不会在这里深入讨论细节,但大多数语言通常都有一个“十进制”方法,可以作为标准库或第三方库来获得更精确的精度。

例如,我发现inf.v0包非常有用。该库的基础是一个Dec结构,它保存指数和整数值。因此,它能够将1.003表示为1003 * 10^-3。请参见下面的示例:

package main

import (
    "fmt"

    "gopkg.in/inf.v0"
)

func main() {
    // represents 1003 * 10^-3
    someDec := inf.NewDec(1003, 3)

    // multiply someDec by 1000 * 10^0
    // which translates to 1003 * 10^-3 * 1000 * 10^0
    someDec.Mul(someDec, inf.NewDec(1000, 0))

    // inf.RoundHalfUp rounds half up in the 0th scale, eg. 0.5 rounds to 1
    value, ok := someDec.Round(someDec, 0, inf.RoundHalfUp).Unscaled()
    fmt.Println(value, ok)
}

希望这有所帮助!

1
我以前没有使用过那个包,但是看文档,似乎在四舍五入后,可以直接调用Unscaled()来提取int值,而不是再通过strcon抓取字符串并进行转换。 - AndrewN
啊是的,你说得对。完全忘了那个。我会更新代码的!谢谢! - Will C

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接