如何在Perl中强制加倍

时间:2015-01-13 23:28:56

标签: perl printf precision long-double

在算术或尝试打印(调试)这么大的数字时,我失去了精确度: 1234567890.123456789

我认为我的问题是$ d(算术结果)和$ e的格式化打印。 我怎么能强迫长双打?我的Perl版本(SUN上的5.8.4)表示它是可能的。 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 个答案:

答案 0 :(得分:2)

Perl有一个大小的浮点数,它被称为NV。构建Perl时会确定NV的大小。

$ perl -V:nvsize
nvsize='8';

此信息也可通过Config模块在Perl程序中访问。

$ perl -MConfig -E'say $Config{nvsize}'
8

构建Perl后,无法更改NV的大小。

在构建Perl时,您可以强制构建Perl以使用long double浮点数,如下所示:

sh Configure -Duselongdouble ...
  -or-
perlbrew install -Duselongdouble ...
  -or-
perlbrew install --ld ...

如果您不想重建perl或重新制作新版本,则需要使用模块。我建议使用Math::LongDouble,因为它提供对本地long double浮动的访问权限,并尽可能透明地进行访问。

另一种选择是使用任意精度的库,例如Math::BigFloat,但如果您需要的只是long double,则速度会慢一些。

答案 1 :(得分:1)

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 Arithmetic Library用于某些操作,您可以获得更好的性能。您必须首先使用正常的CPAN安装过程安装该模块(您不需要GMP)。然后告诉bignum使用GMP。

use bignum lib => "GMP";