Go:将float64转换为带乘数的int

huangapple go评论80阅读模式
英文:

Go: Converting float64 to int with multiplier

问题

我想将一个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...。在Go语言中,如何正确地执行这种操作呢?

英文:

I want to convert a float64 number, let's say it 1.003 to 1003 (integer type). My implementation is simply multiply the float64 with 1000 and cast it to int.

package main

import "fmt"


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

But when I run that code, what I got is 1002 not 1003. Because Go automatically stores 1.003 as 1.002999... in the variable. What is the correct approach to do this kind of operation on Golang?

答案1

得分: 8

Go规范:类型转换:

> 数值类型之间的转换
>
> 当将浮点数转换为整数时,小数部分会被丢弃(向零截断)。

所以基本上当你将浮点数转换为整数时,只保留整数部分。

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

由于float -> int转换不是四舍五入而是保留整数部分,这将给你所期望的结果。考虑到可能的较小和较大表示:

1002.9999 + 0.5 = 1003.4999;     整数部分:1003
1003.0001 + 0.5 = 1003.5001;     整数部分:1003

所以只需写:

var f float64 = 1.003
fmt.Println(int(f * 1000 + 0.5))

将其封装为一个函数:

func toint(f float64) int {
    return int(f + 0.5)
}

// 使用它:
fmt.Println(toint(f * 1000))

Go Playground上尝试它们。

注意:

在处理负数时要小心!例如,如果你有一个值为-1.003,那么你可能希望结果为-1003。但是如果你将0.5加到它上面:

-1002.9999 + 0.5 = -1002.4999;     整数部分:-1002
-1003.0001 + 0.5 = -1002.5001;     整数部分:-1002

所以如果你有负数,你要么:

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

将这个考虑进我们的辅助函数中:

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

Go spec: Conversions:

> Conversions between numeric types
>
> When converting a floating-point number to an integer, the fraction is discarded (truncation towards zero).

So basically when you convert a floating-point number to an integer, only the integer part is kept.

If you just want to avoid errors arising from representing with finite bits, just add 0.5 to the number before converting it to int. No external libraries or function calls (from standard library) required.

Since float -> int conversion is not rounding but keeping the integer part, this will give you the desired result. Taking into consideration both the possible smaller and greater representation:

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

So simply just write:

var f float64 = 1.003
fmt.Println(int(f * 1000 + 0.5))

To wrap this into a function:

func toint(f float64) int {
    return int(f + 0.5)
}

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

Try them on the Go Playground.

Note:

Be careful when you apply this in case of negative numbers! For example if you have a value of -1.003, then you probably want the result to be -1003. But if you add 0.5 to it:

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

So if you have negative numbers, you have to either:

  • subtract 0.5 instead of adding it
  • or add 0.5 but subtract 1 from the result

Incorporating this into our helper function:

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

答案2

得分: 3

如Will所提到的,这取决于浮点数在不同平台上的表示方式。基本上,你需要对浮点数进行四舍五入,而不是让默认的截断行为发生。没有标准库函数来实现这个,可能是因为可能有很多可能的行为,并且实现起来很简单。

如果你知道你总是会有描述的这种错误,即你的值稍微低于(1299.999999)所期望的值(1300.00000),你可以使用math库的Ceil函数:

f := 1.29999
n := math.Ceil(f*1000)

但是,如果你有不同类型的浮点误差,并且想要更一般的排序行为?使用math库的Modf函数将你的浮点数值按小数点分隔:

f := 1.29999

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

你可以在playground中运行一个稍微更通用的版本的这段代码。

英文:

As Will mentions, this comes down to how floats are represented on various platforms. Essentially you need to round the float rather than let the default truncating behavior to happen. There's no standard library function for this, probably because there's a lot of possible behavior and it's trivial to implement.

If you knew you'd always have errors of the sort described, where you're slightly below (1299.999999) the value desired (1300.00000) you could use the math library's Ceil function:

f := 1.29999
n := math.Ceil(f*1000)

But if you have different kinds of floating error and want a more general sorting behavior? Use the math library's Modf function to separate the your floating point value by the decimal point:

f := 1.29999

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

You can run a slightly more generalized version of this code in the playground yourself.

答案3

得分: 0

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

例如,我发现inf.v0包非常有用。该库的底层是一个Dec结构,它保存了指数和整数值。因此,它可以将1.003表示为1003 * 10^-3。以下是一个示例:

package main

import (
	"fmt"

	"gopkg.in/inf.v0"
)

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

	// 将 someDec 乘以 1000 * 10^0
	// 这相当于 1003 * 10^-3 * 1000 * 10^0
	someDec.Mul(someDec, inf.NewDec(1000, 0))

	// inf.RoundHalfUp 在第0位小数上进行四舍五入,例如 0.5 四舍五入为 1
	value, ok := someDec.Round(someDec, 0, inf.RoundHalfUp).Unscaled()
	fmt.Println(value, ok)
}

希望对你有所帮助!

英文:

This is probably likely a problem with floating points in general in most programming languages though some have different implementations than others. I wouldn't go into the intricacies here but most languages usually have a "decimal" approach either as a standard library or a third party library to get finer precision.

For instance, I've found the inf.v0 package largely useful. Underlying the library is a Dec struct that holds the exponents and the integer value. Therefore, it's able to hold 1.003 as 1003 * 10^-3. See below for an example:

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)
}

Hope this helps!

huangapple
  • 本文由 发表于 2015年10月19日 11:11:34
  • 转载请务必保留本文链接:https://go.coder-hub.com/33206059.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定