2010-12-06 3 views
3

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

#!/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 

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

+4

Что каждый компьютерный ученый должен знать о арифметике с плавающей точкой: http://docs.sun.com/source/806-3568/ncg_goldberg.html. Вместо этого вы можете попытаться использовать пары целых чисел как рациональные числа. – nlucaroni 2010-12-06 21:24:23

+2

Фактически вы работаете с числами с плавающей запятой, а не десятичными числами, поскольку этот термин обычно определяется в теории архитектуры. – cdhowie 2010-12-06 21:24:29

ответ

2

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

Округление к нулю с помощью 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 

, а затем рассмотрим, что даже в сторону означает.