浮点表示如何从perl中的十进制表示中取消1?

dla*_*tte 1 floating-point perl decimal

我在一些"humanize_bytes()"代码中遇到了一个有趣的问题.此循环表示没有所有其他逻辑的问题.当字节被截断为"人类可读"级别时,循环需要停止.它继续迭代,直到最终值小于1024(或可指定的字节大小).

当函数输出"1024.0 P"为1024 PB时,我开始研究这个问题.起初我以为我不小心使用了<= vs <,但经过进一步的检查,我发现更有趣的事情正在发生.

此代码重现了该问题.我正在使用perl 5.8.8.

use strict;

my $bytesize = 1024;
my $final = 1152921504606846720;
while (1) {
    printf "bytesize %%d: %d %%f: %s %s final %%d: %19d %%f: %26f\n",
        $bytesize,$bytesize,
        (
            $bytesize == $final ? '==' :
            $bytesize > $final  ? '>'  :
            $bytesize < $final  ? '<'  :
            '<error>'
        ),
        $final,$final;
    last if $final < $bytesize;
    $final /= $bytesize;
}
printf "final = bytesize d:%d f:%s %s final d:%d f:%f\n",
    $bytesize,$bytesize,
    (
        $bytesize == $final ? '==' :
        $bytesize > $final  ? '>'  :
        $bytesize < $final  ? '<'  :
        '<error>'
    ),
    $final,$final;
Run Code Online (Sandbox Code Playgroud)

我收到的输出是:

bytesize %d: 1024 %f: 1024 < final %d: 1152921504606846720 %f: 1152921504606846720.000000
bytesize %d: 1024 %f: 1024 < final %d:    1125899906842623 %f:    1125899906842623.750000
bytesize %d: 1024 %f: 1024 < final %d:       1099511627775 %f:       1099511627775.999756
bytesize %d: 1024 %f: 1024 < final %d:          1073741823 %f:          1073741824.000000
bytesize %d: 1024 %f: 1024 < final %d:             1048575 %f:             1048576.000000
bytesize %d: 1024 %f: 1024 > final %d:                1023 %f:                1024.000000
final = bytesize d:1024 f:1024 > final d:1023 f:1024.000000
Run Code Online (Sandbox Code Playgroud)

这里需要注意的是十进制的最终值是1023,但浮动的是1024.这怎么可能?显然,perl使用十进制表示.

Dig*_*oss 10

您的原始值不是1024 PB,更少256.(我注意到这一点,将它弹出到dc(1)并以十六进制打印:0xFFFFFFFFFFFFF00.)

因此,每次通过循环时,您的数字与您预期的略有不同,最后它会略小一些.

如果你有更高的精确度,你最终会得到

1023.999999999999772626324556767940

这自然地截断到1023并且舍入到1024.

  • 您还应该知道1 exabyte需要61位来表示,IEEE754 double只有53位精度,计算隐藏位. (2认同)