高朗舍入到最近0.05

高朗舍入到最近0.05,第1张

高朗舍入到最近0.05

前言:
我在中发布了此实用程序

github.com/icza/gox
,请参见
mathx.Round()


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.35fmt.Println(Round(3.232, 0.05))    // 3.25fmt.Println(Round(0.4888, 0.05))   // 0.5fmt.Println(Round(-0.363636, 0.05)) // -0.35fmt.Println(Round(-3.232, 0.05))    // -3.25fmt.Println(Round(-0.4888, 0.05))   // -0.5

在Go Playground上尝试一下。

原始答案如下,该答案是在Go
1.10之前创建的(不

math.Round()
存在时),并且还详细说明了自定义
Round()
函数背后的逻辑。在这里出于教育目的。


在Go1.10之前的时代没有

math.Round()
。但…

舍入任务可以很容易地通过

float64
=>
int64
转换来实现,但是必须小心,因为float到int的转换 不是舍入而是保留整数部分

例如:

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

12
在两种情况下,结果都是整数部分。要获取四舍五入的“功能”,只需添加
0.5

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

到目前为止,一切都很好。但是我们不想舍入到整数。如果我们想四舍五入到小数点后一位,则在添加

0.5
和转换之前将乘以10 :

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

因此,基本上,您要乘以要舍入的单位的倒数。要舍入为

0.05
单位,请乘以
1/0.05 = 20

f = 12.31fmt.Println(float64(int64(f*20+0.5)) / 20) // 12.3f = 12.66fmt.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.35fmt.Println(Round(3.232, 0.05))    // 3.25fmt.Println(Round(0.4888, 0.05))   // 0.5

在Go Playground上尝试示例。

需要注意的是四舍五入

3.232
unit=0.05
不准确打印
3.25
,但
0.35000000000000003
。这是因为
float64
数字使用称为IEEE-754标准的有限精度进行存储。

另请注意,它

unit
可以是“任何”数字。如果为
1
,则
Round()
基本上四舍五入为最接近的整数。如果为
10
,则四舍五入为
0.01
十进制;如果为,则四舍五入为2个小数位。

还要注意,当您

Round()
使用负数拨打电话时,您可能会得到令人惊讶的结果:

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

这是因为-如前所述,转换将保留整数部分,例如

-1.6
is的整数部分
-1
(大于
-1.6
;而
1.6
is的整数部分
1
小于
1.6
)。

如果要

-0.363636
成为
-0.35
而不是
-0.30
,则在负数的情况下在函数内部添加
-0.5
而不是。请参阅我们改进的功能:
0.5``Round()``Round2()

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.35fmt.Println(Round2(-3.232, 0.05))    // -3.25fmt.Println(Round2(-0.4888, 0.05))   // -0.5

编辑:

为了解决您的评论:由于您不喜欢“不精确”

0.35000000000000003
,因此建议将其格式化并重新解析为:

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

而这种“貌似”的结果与打印出来的结果

0.35
完全一样。

但这只是一个“幻想”。由于

0.35
不能使用IEEE-754标准用有限的位表示,因此处理该数字也没关系,如果将其存储在type值中
float64
,则该数字将不是完全正确的
0.35
(但是IEEE-754数字非常接近对此)。您看到的是将其
fmt.Println()
打印出来,
0.35
因为
fmt.Println()
已经进行了一些四舍五入。

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

fmt.Printf("%.30fn", Round(0.363636, 0.05))fmt.Printf("%.30fn", Round(3.232, 0.05))fmt.Printf("%.30fn", Round(0.4888, 0.05))

输出:不好(可能更难看):在Go Playground上尝试:

0.3499999999999999777955395074973.2500000000000000000000000000000.500000000000000000000000000000

注意,另一方面,

3.25
0.5
是精确的,因为它们可以精确地用有限位表示,因为用二进制表示:

3.25 = 3 + 0.25 = 11.01binary0.5 = 0.1binary

有什么教训?格式化和重新解析结果是不值得的,因为它也不是精确的(只是一个不同的

float64
值(根据默认的
fmt.Println()
格式化规则)在打印时可能会更好)。如果您想要好的打印格式,则只需进行精确的格式化即可,例如:

func main() {    fmt.Printf("%.3fn", Round(0.363636, 0.05))    fmt.Printf("%.3fn", Round(3.232, 0.05))    fmt.Printf("%.3fn", Round(0.4888, 0.05))}func Round(x, unit float64) float64 {    return float64(int64(x/unit+0.5)) * unit}

这将是准确的(在Go Playground上尝试):

0.3503.2500.500

或者只是将它们相乘

100
并使用整数,这样就不会发生任何表示或舍入错误。



欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/4934705.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-11-13
下一篇 2022-11-13

发表评论

登录后才能评论

评论列表(0条)

保存