在外壳浮点舍入(Floating point rounding in shell)

2019-09-21 15:28发布

$ printf "%0.2f\n" 41.495
41.49
$ printf "%0.2f\n" 41.485
41.49
$ printf "%0.2f\n" 41.475
41.47
$ printf "%0.2f\n" 41.465
41.47
$ printf "%0.2f\n" 41.455
41.46
$ printf "%0.2f\n" 41.445
41.44
$ printf "%0.2f\n" 41.435
41.44
$ printf "%0.2f\n" 41.425
41.42
$ printf "%0.2f\n" 41.415
41.42
$ printf "%0.2f\n" 41.405
41.40

为什么与奇数号码作为第二位小数不正确的舍入,甚至那些是谁? 另外什么是错的0.445它永远不会被四舍五入?

Answer 1:

它有浮点做,但不能与双精度。

当你写

printf "%0.2f\n" 41.495

在系统上, printf第一轮41.495到最近的x87 80位浮点数[1]。 这是如何运作的? 先写在二进制41.495:

b101001.0111 11101011100001010001 11101011100001010001 11101011100001010001 ...

(分离群组重复循环往复)。 现在,我们这一轮号码有64位二进制数字:

b101001.0111111010111000010100011110101110000101000111101011100001

这是一个实际上是由printf的格式化的数字。 用十进制,正是:

41.4949999999999999990285548534529880271293222904205322265625

你可以看到,它比41.495少只是一点点,所以当它的printf以四舍五入保留两位小数,但几轮下来 ,和41.49打印。

现在看41.485; 取整到64位二进制数字后,我们得到的值:

41.48500000000000000055511151231257827021181583404541015625

这比41.485 只是一点点,所以printf的回合起来

在我的系统,有一个关于这个在printf的预警管理:

由于浮点数从ASCII转换为浮点,然后再回来,浮点精度可能会丢失。


  1. bash中不使用的所有操作系统(事实上,它甚至不是所有架构都支持)的格式的x87; 在其它一些系统,这些值将被解释为双打(因此舍入为53位,而不是64),其结果将是不同的。


Answer 2:

我躺在大赔率它与做IEEE双精度浮点类型。 长和短的那就是任何十进制数在内部与指数和分数分量表示,但不是在小数,但在二进制。 这不是一个100%的解释,文章解释了它好很多,但基本上浮点数来表示“接近”他们是如何,你键入的不一定正是'。因此,舍入可以得到有点奇怪为好。

阅读维基文章。 这应该帮助。 如果你需要精确,来看一下,不使用这个标准的其它数字表示。



Answer 3:

壳层或printf命令可以是使用扩展精度浮点数,如Intel的80位浮点。 printf是在一些壳,直接实现,并且可以作为一个单独的可执行文件,如在/usr/bin/printf

最接近的单精度值(IEEE 754),以41.495是41.494998931884765625。 因此,当文本“41.495”被解释为单精度值,它代表的正是41.494998931884765625。 当此值小数点后四舍五入至小数点后两位数,这是41.49,因为“499 ......”几轮下来。

最接近的扩展精度值到41.495是41.4949999999999999990285548534529880271293222904205322265625。 因此,当文本“41.495”的解释,它代表的正是41.4949999999999999990285548534529880271293222904205322265625。 当这是小数点后四舍五入至小数点后两位数,这是41.49。

最接近的扩展精度值到41.485是41.48500000000000000055511151231257827021181583404541015625。 当圆形,这是41.49。

最接近的扩展精度值到41.475是41.474999999999999998612221219218554324470460414886474609375。 当圆形,这是41.47。

最接近的扩展精度值到41.465是41.4650000000000000001387778780781445675529539585113525390625。 当圆形,这是41.47。

最接近的扩展精度值到41.455是41.45500000000000000166533453693773481063544750213623046875。 当圆形,这是41.46。

最接近的扩展精度值到41.445是41.444999999999999999722444243843710864894092082977294921875。 当圆形,这是41.44。

最接近的扩展精度值到41.435是41.4350000000000000012490009027033011079765856266021728515625。 当圆形,这是41.44。

最接近的扩展精度值到41.425是41.4249999999999999993061106096092771622352302074432373046875。 当圆形,这是41.42。

最接近的扩展精度值到41.415是41.415000000000000000832667268468867405317723751068115234375。 当圆形,这是41.42。

最接近的扩展精度值到41.405是41.4049999999999999988897769753748434595763683319091796875。 当圆形,这是41.40。



Answer 4:

你可以有庆典演出,你到底是什么,在内部做: printf "%0.20f\n" 41.495通过试验和错误,注册表的不同表示下一个最小号码是: printf "%0.20f\n" 41.495000000000000001 ,这实际上是比双精度更加精确。 其实,这一切的printf命令。 庆典实际上不理解浮点数。



文章来源: Floating point rounding in shell