Почему я не получаю ноль, когда вычитаю из себя одно и то же число с плавающей запятой в Perl?

Возможные дубликаты:
Почему плавающий точечная арифметика в C # неточна?
Почему ghci говорит, что 1.1 + 1.1 + 1.1› 3.3 верно?

#!/usr/bin/perl
$l1 = "0+0.590580+0.583742+0.579787+0.564928+0.504538+0.459805+0.433273+0.384211+0.3035810";
$l2 = "0+0.590580+0.583742+0.579788+0.564928+0.504538+0.459805+0.433272+0.384211+0.3035810";
$val1 = eval ($l1);
$val2 = eval ($l2);
$diff = (($val1 - $val2)/$val1)*100;
print " (($val1 - $val2)/$val1)*100 ==> $diff\n";

Удивительно, но результат оказался

((4.404445 - 4.404445)/4.404445)*100 ==> -2.01655014354845e-14.

Разве это не должно быть НУЛЯ ???? Кто-нибудь может объяснить это, пожалуйста ...


person Prashant    schedule 17.01.2010    source источник
comment
Что, вы не думаете, что 0,000000000000201655014354845 достаточно близко к нулю?   -  person Ether    schedule 17.01.2010
comment
Этот вопрос не полностью повторяет процитированные вопросы, не относящиеся к Perl, потому что вопрос усложняется тем, что perl обеспечивает точность строкового преобразования по умолчанию, немного меньшую, чем доступная числовая точность.   -  person ysth    schedule 18.01.2010


Ответы (4)



Это довольно близко к нулю, чего я и ожидал.

Почему он должен быть нулевым? 0,579787! = 0,579788 и 0,433273! = 0,433272. Скорее всего, ни один из них не имеет точного представления с плавающей запятой, поэтому вам следует ожидать некоторых неточностей.

person CB Bailey    schedule 17.01.2010

Из ответа perlfaq4 на Почему я получаю длинные десятичные дроби (например, 19.9499999999999) вместо чисел, которые я должен получать (например, 19.95)?:


Внутренне ваш компьютер представляет числа с плавающей запятой в двоичном формате. Цифровые (как степень двойки) компьютеры не могут точно хранить все числа. Некоторые действительные числа при этом теряют точность. Это проблема того, как компьютеры хранят числа, и влияет на все компьютерные языки, а не только на Perl.

perlnumber показывает кровавые подробности представления чисел и преобразований.

Чтобы ограничить количество десятичных знаков в числах, вы можете использовать функцию printf или sprintf. См. «Арифметика с плавающей запятой» для получения более подробной информации.

printf "%.2f", 10/3;

my $number = sprintf "%.2f", 10/3;
person brian d foy    schedule 17.01.2010
comment
Это своего рода оборотная сторона этого perlfaq; см. мой комментарий к вопросу. - person ysth; 18.01.2010

Когда вы меняете две строки на равные (между $l1 и $l2 различаются 2 цифры), это действительно приводит к нулю.

Это демонстрирует то, что вы можете создать 2 разных числа с плавающей запятой ($val1 и $val2), которые выглядят одинаково при распечатке, но имеют небольшую разницу внутри. Эти различия могут быть увеличены, если вы не будете осторожны.

Винко Врсалович опубликовал несколько хороших ссылок, чтобы объяснить, почему.

person FalseVinylShrub    schedule 17.01.2010