Как представление с плавающей запятой может отличаться от 1 от десятичного представления в Perl?

Я столкнулся с интересной проблемой в моем коде "humanize_bytes()". Этот цикл представляет проблему без всякой другой логики. Цикл необходимо остановить, когда байты будут усечены до «удобочитаемого» уровня. Он продолжает повторяться до тех пор, пока окончательное значение не станет меньше 1024 (или заданного размера в байтах).

Я начал разбираться в проблеме, когда функция выдала "1024.0 P" для 1024 петабайт. Сначала я подумал, что случайно использую ‹= vs ‹, но при дальнейшем рассмотрении я обнаружил, что происходит нечто более интересное.

Этот код воспроизводит проблему. Я использую перл 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;

Вывод, который я получаю:

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

Здесь следует обратить внимание на то, что конечное значение в десятичном виде равно 1023, а в вещественном — 1024. Как такое может быть? И, очевидно, Perl использует десятичное представление.


person dlamotte    schedule 17.11.2009    source источник


Ответы (2)


Похоже, что Perl округляет значение, переданное в %f. Если вы завернете свое значение в int($final), вы получите вывод 1023, указывающий, что оператор %d делает правильную вещь (всегда округляя до ближайшего целого числа).

person PP.    schedule 17.11.2009

Ваше исходное значение не 1024 петабайта, а на 256 меньше. (Я заметил это, вставив его в dc(1) и напечатав его в шестнадцатеричном формате. : 0xFFFFFFFFFFFFFF00.)

Следовательно, каждый раз при прохождении цикла ваше число немного отличается от ожидаемого, и в конце концов оно становится немного меньше.

Если бы у вас было больше точности, вы бы получили

1023.999999999999772626324556767940

Это естественно усекается до 1023 и округляется до 1024.

person DigitalRoss    schedule 17.11.2009
comment
Хотя вы также правы, я думаю, что ответ PP лучше описывает, что происходит с различными задействованными строками формата, что больше соответствует тому, что задавал мой вопрос. - person dlamotte; 17.11.2009
comment
Вы также должны знать, что для представления 1 эксабайта требуется 61 бит, а двойник IEEE754 имеет только 53 бита точности, считая скрытый бит. - person DigitalRoss; 17.11.2009