如何在 Perl 中强制使用 long double

How to force long double in Perl

我在做算术或尝试打印(调试)这么大的数字时失去了精度: 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);

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

use bignum lib => "GMP";

Perl 有一种 float 大小,叫做 NVNV 的大小在构建 Perl 时决定。

$ 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.

,那会比必要的慢