Golang最接近0.05 [英] Golang Round to Nearest 0.05

查看:112
本文介绍了Golang最接近0.05的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在寻找一个函数,在Golang中精确到0.05。
使用函数的最终结果必须是0.05的因子。

输出为我所寻找的功能:
(函数Round不存在,我希望它可以包含在答案中)

<$ p $ $ 0.35
Round(3.232)// 3.25
Round(0.4888)// 0.5
pre>

我已经搜遍了很多年,现在还没有找到任何答案。

h2_lin>解决方案

Go 1.10已发布,并添加了 math.Round() 函数。这个函数四舍五入到最接近的整数(这基本上是一个到最接近的1.0的操作),并且使用这个函数我们可以非常容易地构造一个函数,以四舍五入到我们选择的单位:

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

测试它:

  fmt.Println(Round(0.363636,0.05))// 0.35 
fmt.Println(Round(3.232,0.05))// 3.25
fmt.Println(Round(0.4888 ,0.05))// 0.5

fmt.Println(Round(-0.363636,0.05))// -0.35
fmt.Println(Round(-3.232,0.05))// - 3.25
fmt.Println(Round(-0.4888,0.05))// -0.5

试试去游乐场



原来的答案如下,它是在Go 1.10之前创建的,当时没有 math.Round()存在,并且还详细说明了我们自定义 Round()函数。这是为了教育目的。






在Go1.10时代之前,没有数学.Round()。但是...
$ b

舍入任务很容易通过 float64 => int64 converison,但必须小心,因为float to int转换不是舍入,而是保留整数部分(详见 Go:将float64转换为带有乘数的int )。

例如:

  var f float64 
f = 12.3
fmt.Println(int64(f))// 12
f = 12.6
fmt.Println(int64(f))// 12

在这两种情况下,结果都是 12 ,即整数部分。要获得舍入功能,只需添加 0.5

  f = 12.3 
fmt.Println(int64(f + 0.5))// 12
f = 12.6
fmt.Println(int64(f + 0.5))// 13

到目前为止这么好。但我们不想整数。如果我们想要舍入到1个小数位,我们将乘以10再加上 0.5 并转换:

  f = 12.31 
fmt.Println(float64(int64(f * 10 + 0.5))/ 10)// 12.3
f = 12.66
fmt。 Println(float64(int64(f * 10 + 0.5))/ 10)// 12.7

你乘以你想要回合的单位的倒数。要舍入 0.05 单位,乘以 1 / 0.05 = 20

  f = 12.31 
fmt.Println(float64(int64(f * 20 + 0.5))/ 20)// 12.3
f = 12.66
fmt.Println(float64(int64(f * 20 + 0.5))/ 20)// 12.65



<

  func Round(x,unit float64)float64 {
return float64(int64 (x / unit + 0.5))* unit
}

使用它:

  fmt.Println(Round(0.363636,0.05))// 0.35 
fmt.Println(Round(3.232,0.05)) // 3.25
fmt.Println(Round(0.4888,0.05))// 0.5

试试去游乐场



请注意,使用 unit = 0.05 四舍五入 3.232 不会完全打印 3.25 code>但是 0.35000000000000003 。这是因为 float64 数字以有限精度存储,称为 IEEE-754 标准。有关详细信息,请参阅 Golang将float64转换为int错误



另请注意,单位可能是任意数字。如果它是 1 ,那么 Round()基本上四舍五入到最接近的整数。如果它是 10 ,它会变成数十,如果它是 0.01 ,它会变成2个小数位。



另外请注意,当您调用负数的 Round()时,您可能会得到令人惊讶的结果:

  fmt.Println(Round(-0.363636,0.05))// -0.3 
fmt.Println(Round(-3.232,0.05)) // -3.2
fmt.Println(Round(-0.4888,0.05))// -0.45

这是因为 - 如前所述,转换保留整数部分,例如 -1.6 的整数部分是 -1 (它大于 -1.6 ;而 1.6 的整数部分是 1 小于 1.6 )。



如果你想要 -0.363636 变成 -0.35 而不是 -0.30 ,那么的负数在 Round()内添加 -0.5 而不是 0.5 $ c>函数。
$ b

  func Round2(x, unit float64)float64 {
if x> 0 {
return float64(int64(x / unit + 0.5))* unit
}
return float64(int64(x / unit-0.5))* unit
}

使用它:

  fmt.Println(Round2(-0.363636,0.05))// -0.35 
fmt.Println(Round2(-3.232,0.05))// -3.25
fmt.Println(Round2 (-0.4888,0.05))// -0.5






编辑:



要解决您的评论:因为您不喜欢 0.35000000000000003 ,你建议格式化并重新解析它:

  formatted,err:= strconv。 ParseFloat(fmt.Sprintf(%。2f,四舍五入),64)

打印结果的确切结果是 0.35



但这只是一种错觉。由于 0.35 不能用IEEE-754标准的有限位表示,因此如果将它存储在类型 float64 ,它不会完全是 0.35 (但是IEEE-754数字非常接近它)。你看到 fmt.Println()打印为 0.35 ,因为 fmt.Println )



但是,如果您尝试以更高的精确度打印它:

  fmt.Printf(%。30f\\\
,Round(0.363636,0.05))
fmt.Printf(%。30f\\\
, Round(3.232,0.05))
fmt.Printf(%。30f\\\
,Round(0.4888,0.05))

输出:它不是更好(可能更丑):试试它在

  0.349999999999999977795539507497 
3.250000000000000000000000000000
0.500000000000000000000000000000
code>

请注意,另一方面 3.25 0.5 是精确的,因为它们可以用有限位精确表示,因为用二进制表示:

  3.25 = 3 + 0.25 = 11.01二元
0.5 = 0.1二元

什么是教训?这是不值得格式化和重新解析的结果,因为它不会是确切的(只是一个不同的 float64 值 - 这是根据默认值 fmt .Println()格式化规则 - 在打印时可能更好)。如果你想要很好的打印格式,只需要精确格式化,如:

  func main(){
fmt.Printf (%.3f\\\
,Round(0.363636,0.05))
fmt.Printf(%。3f\\\
,Round(3.232,0.05))
fmt.Printf( %.3f\\\
,Round(0.4888,0.05))
}

func Round(x,unit float64)float64 {
return float64(int64(x / unit +0.5))* unit
}

Go Playground ):

  0.350 
3.250
0.500

将它们乘以 100 并使用整数,这样就不会出现表示或舍入错误。


I am looking for a function to round to the nearest 0.05 in Golang. The end result of using the function must always be a factor of 0.05.


Here are some examples of outputs for the function I am looking for: (The function Round doesn't exist yet, I am hoping it can be included in the answer)

Round(0.363636) // 0.35
Round(3.232)    // 3.25
Round(0.4888)   // 0.5

I have searched around for ages now and haven't found any answers.

解决方案

Go 1.10 has been released, and it adds a math.Round() function. This function rounds to the nearest integer (which is basically a "round to nearest 1.0" operation), and using that we can very easily construct a function that rounds to the unit of our choice:

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

Testing it:

fmt.Println(Round(0.363636, 0.05)) // 0.35
fmt.Println(Round(3.232, 0.05))    // 3.25
fmt.Println(Round(0.4888, 0.05))   // 0.5

fmt.Println(Round(-0.363636, 0.05)) // -0.35
fmt.Println(Round(-3.232, 0.05))    // -3.25
fmt.Println(Round(-0.4888, 0.05))   // -0.5

Try it on the Go Playground.

The original answer follows which was created before Go 1.10 when no math.Round() existed, and which also details the logic behind our custom Round() function. It's here for educational purposes.


In the pre-Go1.10 era there was no math.Round(). But...

Rounding tasks can easily be implemented by a float64 => int64 converison, but care must be taken as float to int conversion is not rounding but keeping the integer part (see details in Go: Converting float64 to int with multiplier).

For example:

var f float64
f = 12.3
fmt.Println(int64(f)) // 12
f = 12.6
fmt.Println(int64(f)) // 12

Result is 12 in both cases, the integer part. To get the rounding "functionality", simply add 0.5:

f = 12.3
fmt.Println(int64(f + 0.5)) // 12
f = 12.6
fmt.Println(int64(f + 0.5)) // 13

So far so good. But we don't want to round to integers. If we'd wanted to round to 1 fraction digit, we would multiply by 10 before adding 0.5 and converting:

f = 12.31
fmt.Println(float64(int64(f*10+0.5)) / 10) // 12.3
f = 12.66
fmt.Println(float64(int64(f*10+0.5)) / 10) // 12.7

So basically you multiply by the reciprocal of the unit you want to round to. To round to 0.05 units, multiply by 1/0.05 = 20:

f = 12.31
fmt.Println(float64(int64(f*20+0.5)) / 20) // 12.3
f = 12.66
fmt.Println(float64(int64(f*20+0.5)) / 20) // 12.65

Wrapping this into a function:

func Round(x, unit float64) float64 {
    return float64(int64(x/unit+0.5)) * unit
}

Using it:

fmt.Println(Round(0.363636, 0.05)) // 0.35
fmt.Println(Round(3.232, 0.05))    // 3.25
fmt.Println(Round(0.4888, 0.05))   // 0.5

Try the examples on the Go Playground.

Note that rounding 3.232 with unit=0.05 will not print exactly 3.25 but 0.35000000000000003. This is because float64 numbers are stored using finite precision, called the IEEE-754 standard. For details see Golang converting float64 to int error.

Also note that unit may be "any" number. If it's 1, then Round() basically rounds to nearest integer number. If it's 10, it rounds to tens, if it's 0.01, it rounds to 2 fraction digits.

Also note that when you call Round() with a negative number, you might get surprising result:

fmt.Println(Round(-0.363636, 0.05)) // -0.3
fmt.Println(Round(-3.232, 0.05))    // -3.2
fmt.Println(Round(-0.4888, 0.05))   // -0.45

This is because –as said earlier– conversion is keeping the integer part, and for example integer part of -1.6 is -1 (which is greater than -1.6; while integer part of 1.6 is 1 which is less than 1.6).

If you want -0.363636 to become -0.35 instead of -0.30, then in case of negative numbers add -0.5 instead of 0.5 inside the Round() function. See our improved Round2() function:

func Round2(x, unit float64) float64 {
    if x > 0 {
        return float64(int64(x/unit+0.5)) * unit
    }
    return float64(int64(x/unit-0.5)) * unit
}

And using it:

fmt.Println(Round2(-0.363636, 0.05)) // -0.35
fmt.Println(Round2(-3.232, 0.05))    // -3.25
fmt.Println(Round2(-0.4888, 0.05))   // -0.5


EDIT:

To address your comment: because you don't "like" the non-exact 0.35000000000000003, you proposed to format it and re-parse it like:

formatted, err := strconv.ParseFloat(fmt.Sprintf("%.2f", rounded), 64)

And this "seemingly" results in the exact result as printing it gives 0.35 exactly.

But this is just an "illusion". Since 0.35 cannot be represented with finite bits using IEEE-754 standard, doesn't matter what you do with the number, if you store it in a value of type float64, it won't be exactly 0.35 (but an IEEE-754 number being very close to it). What you see is fmt.Println() printing it as 0.35 because fmt.Println() already does some rounding.

But if you attempt to print it with higher precision:

fmt.Printf("%.30f\n", Round(0.363636, 0.05))
fmt.Printf("%.30f\n", Round(3.232, 0.05))
fmt.Printf("%.30f\n", Round(0.4888, 0.05))

Output: it's not nicer (might be even uglier): try it on the Go Playground:

0.349999999999999977795539507497
3.250000000000000000000000000000
0.500000000000000000000000000000

Note that on the other hand 3.25 and 0.5 are exact because they can be represented with finite bits exactly, because representing in binary:

3.25 = 3 + 0.25 = 11.01binary
0.5 = 0.1binary

What's the lesson? It's not worth formatting and re-parsing the result, as it won't be exact either (just a different float64 value which –according to default fmt.Println() formatting rules– might be nicer in printing). If you want nice printed format, just format with precision, like:

func main() {
    fmt.Printf("%.3f\n", Round(0.363636, 0.05))
    fmt.Printf("%.3f\n", Round(3.232, 0.05))
    fmt.Printf("%.3f\n", Round(0.4888, 0.05))
}

func Round(x, unit float64) float64 {
    return float64(int64(x/unit+0.5)) * unit
}

And it will be exact (try it on the Go Playground):

0.350
3.250
0.500

Or just multiply them by 100 and work with integer numbers, so that no representation or rounding error may occur.

这篇关于Golang最接近0.05的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆