Как заставить длинный дубль в Perl
Я теряю точность, когда делаю арифметику или пытаюсь напечатать (отладить) такие большие цифры: 1234567890.123456789
Я думаю, что мои проблемы связаны с $d (результат арифметики) и отформатированной печатью $e. Как я могу заставить длинные пары? Моя версия Perl (5.8.4 на SUN) говорит, что это возможно. У sprintf есть опция размера для длинных двойных чисел (q или L или ll), но я не выяснил, как ее использовать, и не знаю, будет ли она работать с printf.
Изменить: я добавил BigFloat, который работает! Но я все еще хотел бы форсировать длинные пары.
Попробуйте добавить 1234567890 + 0.123456789 и вычесть 1234567890 - 0.123456789.
use Config;
use Math::BigFloat;
$a = 1234567890;
$b = 123456789;
$c = $b/1e9; # 0.123456789
$d = $a + $c; # not enough precision (32-bit or double?)
$e = sprintf("%d.%.9d",$a,$b); # combine as strings
$f = 1234567890.123456789; # for reference (not enough precision)
# Use BigFloat to bypass lack of longdbl
$aBig = Math::BigFloat->new("$a");
$dSum = $aBig->fadd("$c"); # $dSum = $a + $c
$aBig = Math::BigFloat->new("$a"); # <-- Need a new one for every operation?
$dDif = $aBig->fsub(abs("$c")); # $dDif = $a - $c
print "a $a\n"; # 1234567890
print "c $c\n"; # 0.123456789
print "d=a+c $d\n"; # 1234567890.12346 <-- **Problem**
print "dSum=a+c $dSum\n"; # 1234567890.123456789 <-- Solution
print "dDif=a-c $dDif\n"; # 1234567890.876543211 <-- Solution
print "e $e\n"; # 1234567890.123456789
print "f $f\n"; # 1234567890.12346 <-- double, 52-bit, not longdbl?
printf ("printf e 20.9f %20.9f\n",$e); # 1234567890.123456717 <-- **Problem**
printf ("printf dSum 20.9f %20.9f\n",$dSum); # 1234567890.123456717 <-- **Problem**
printf ("printf dSum 20s %20s\n",$dSum); # 1234567890.123456789
printf ("printf dDif 20.9f %20.9f\n",$dDif); # 1234567890.876543283 <-- **Problem**
printf ("printf dDif 20s %20s\n",$dDif); # 1234567890.876543211
print "uselongdouble $Config{uselongdouble}\n"; # empty. No long doubles by default
print "d_longdbl $Config{d_longdbl}\n"; # "define". Supports long doubles
print "size double longdbl $Config{doublesize} $Config{longdblsize}\n"; # Ans 8 16
Я также использовал этот код, чтобы попытаться понять типы, но это не сильно помогло. Кто-нибудь использовал это, чтобы объяснить подобные проблемы?
use Devel::Peek 'Dump';
Dump ($dSum); # Wow, it's complicated
Dump ($f);
2 ответа
bignum перегрузит все операторы в текущей области для использования целых чисел произвольной точности и операций с плавающей запятой.
use bignum;
my $f = 123456789.123456789;
print "$f\n"; # 123456789.123456789
print $f + $f, "\n"; # 246913578.246913578
За кулисами bignum превращает все числовые константы в объекты Math:: BigInt и Math::BigNum в зависимости от ситуации.
Важно отметить, что bignum имеет лексическую область и не влияет на всю программу. Например...
{
use bignum;
$f = 123456789.123456789; # $f is a Math::BigNum object
}
$g = 123456789.123456789; # $g is a regular NV
print "$f\n"; # 123456789.123456789
print "$g\n"; # 123456789.123457
# This will use Math::BigNum's addition method, but $g has already lost precision.
print $f + $g, "\n"; # 246913578.246913789
Вы можете повысить производительность, используя плагин Math:: BigInt:: GMP для использования арифметической библиотеки GNU Multiple Precision для некоторых операций. Сначала вы должны установить этот модуль, используя обычный процесс установки CPAN (вам не нужен GMP). Тогда скажите bignum использовать GMP.
use bignum lib => "GMP";
Perl имеет один размер с плавающей точкой, и он называется NV
, Размер NV
решено, когда Perl построен.
$ perl -V:nvsize
nvsize='8';
Эта информация также доступна в программе Perl через модуль Config.
$ perl -MConfig -E'say $Config{nvsize}'
8
Размер NV
не может быть изменено после сборки Perl.
Вы можете заставить Perl быть собранным для использования long double
плавает следующим образом при сборке Perl:
sh Configure -Duselongdouble ...
-or-
perlbrew install -Duselongdouble ...
-or-
perlbrew install --ld ...
Если вы не хотите восстанавливать свой perl
или сделать новый, вам нужно будет использовать модуль. Я рекомендую Math:: LongDouble, поскольку он обеспечивает доступ к нативному long double
плавает и делает это максимально прозрачно.
Другой вариант - использовать библиотеку произвольной точности, такую как Math:: BigFloat, но это будет медленнее, чем необходимо, если все, что вам нужно, это long double
,