6
$ 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

为什么第二位小数为奇数的数字没有正确四舍五入,而偶数却是?此外,.445 永远不会四舍五入有什么问题?

4

4 回答 4

7

它与浮点有关,但与双精度无关。

当你写

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 位),结果会有所不同。
于 2012-08-24T17:22:48.983 回答
2

我认为它与IEEE 双精度浮点类型有关。总而言之,任何十进制数在内部都用指数和分数分量表示,但不是用小数表示,而是用二进制表示。这不是 100% 的解释,文章解释得更好,但基本上浮点数的表示“接近”它们的样子,不一定完全是你输入的内容。因此,四舍五入也会有点奇怪。

阅读维基文章。那应该有帮助。如果您需要精确性,请查看不使用此标准的其他数字表示。

于 2012-08-24T16:59:35.917 回答
2

您的 shell 或 printf 命令可能正在使用扩展精度浮点,例如 Intel 的 80 位浮点。printf直接在某些 shell 中实现,并可作为单独的可执行文件使用,例如在/usr/bin/printf.

最接近 41.495 的单精度值(在 IEEE 754 中)是 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。

于 2012-08-24T17:26:17.810 回答
0

您可以让 bash 准确地向您展示它在内部所做的事情:printf "%0.20f\n" 41.495通过反复试验,注册不同表示的下一个最小数字是: printf "%0.20f\n" 41.495000000000000001,它实际上比双精度更精确。实际上,这都是 printf 命令。bash 实际上并不理解浮点数。

于 2012-08-24T18:01:43.100 回答