Округление десятичных чисел в perl, неверный результат - PullRequest
3 голосов
/ 07 декабря 2010

Я ненавижу десятичные числа. Для 1.005 я не получаю ожидаемый результат со следующим кодом.

#!/usr/bin/perl -w

use strict;
use POSIX qw(floor);

my $num = (1.005 * 100) + 0.5;
print $num . "\n";          # 101
print floor($num) . "\n";   # 100
print int($num) . "\n";     # 100

Для 2.005 и 3.005 работает нормально.

С этим безобразным "хаком" я получаю ожидаемый результат.

#!/usr/bin/perl -w

use strict;
use POSIX qw(floor);

my $num = (1.005 * 100) + 0.5;
$num = "$num";
print $num . "\n";          # 101
print floor($num) . "\n";   # 101
print int($num) . "\n";     # 101

Как правильно это сделать?

Ответы [ 2 ]

3 голосов
/ 07 декабря 2010

floor() не для округления, оно идет до ближайшего целого числа.

Смотрите этот старый пост: Как вы округляете число с плавающей запятой в Perl?

2 голосов
/ 07 декабря 2010

«Правильный» способ округления - это любой способ , который вы выберете для определения правильного для любой цели, которую вы имеете в виду. Люди много думали об этом, и некоторые стратегии больше подходят для одних областей применения, чем для других.

Округление до нуля с помощью int() - это редко, что люди хотят. Обычно они хотят чего-то беспристрастного в численном отношении.

Ярмарка разбивает пятерки; отсюда сокращенный термин «круглый к четному». Ближайшее целое число не определено: нет ближайшего целого числа, когда наименьшая цифра равна 5. Есть 9 вещей, которые при округлении дают другой ответ: 1,2,3,4,5,6 , 7,8,9.

Чтобы быть справедливым, половина из них должна идти в одну сторону, а половина - в другую. Но есть девять чисел, так что у вас четыре идут в одну сторону, а четыре идут в другую, но теперь у вас есть проблема справедливости. Таким образом, единственный способ избежать смещения состоит в том, чтобы 5 случаев чередовались вверх и вниз. Вот почему это работает так.

% perl -e 'printf "%.0f\n", $_+.5 for -10 .. +10'

производит последовательность

-10 -8 -8 -6 -6 -4 -4 -2 -2 -0 0 2 2 4 4 6 6 8 8 10 10

При приближении к нулю:

% perl -e 'print int($_+.5)," " for -10 .. +10; print "\n"'

делает это с горбом посередине:

 -9 -8 -7 -6 -5 -4 -3 -2 -1 0 0 1 2 3 4 5 6 7 8 9 10 

Снова посмотрим на округление-к-четному:

% perl -le 'printf "%.1f ", $_+.05 for -10 .. +10'

-9.9 -8.9 -8.0 -7.0 -6.0 -5.0 -4.0 -3.0 -1.9 -0.9 0.1 1.1 2.0 3.0 4.0 5.0 6.0 7.0 8.1 9.1 10.1 

Это имеет больше смысла, если вы сделаете это:

% perl -le 'printf "%.2f ", $_+.05 for -10 .. +10'

-9.95 -8.95 -7.95 -6.95 -5.95 -4.95 -3.95 -2.95 -1.95 -0.95 0.05 1.05 2.05 3.05 4.05 5.05 6.05 7.05 8.05 9.05 10.05 

, а затем подумайте, что значит даже.

...