Pha*_*gun 3 floating-point perl
使用不同版本的perl运行时,下面的代码片段给出了不同的输出:
#!/usr/bin/env perl
my $number1 = 2.198696207;
my $number2 = 2.134326286;
my $diff = $number1 - $number2;
print STDOUT "\n 2.198696207 - 2.134326286: $diff\n";
$number1 = 0.449262271;
$number2 = 0.401361096;
$diff = $number1 - $number2;
print STDOUT "\n 2.198696207 - 2.134326286: $diff\n";
Run Code Online (Sandbox Code Playgroud)
PERL 5.16.3: -
perl -v
This is perl 5, version 16, subversion 3 (v5.16.3) built for x86_64-linux
file `which perl`
/sv/app/perx/third-party/bin/perl: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.18, not stripped
2.198696207 - 2.134326286: 0.0643699210000004
2.198696207 - 2.134326286: 0.047901175
Run Code Online (Sandbox Code Playgroud)
PERL 5.8.7: - perl -v
This is perl, v5.8.7 built for i686-linux-thread-multi-64int
file `which perl`
/sv/app/perx/third-party/bin/perl: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2.2.5, dynamically linked (uses shared libs), for GNU/Linux 2.2.5, not stripped
2.198696207 - 2.134326286: 0.0643699209999999
2.198696207 - 2.134326286: 0.047901175
Run Code Online (Sandbox Code Playgroud)
我无法找到任何说明上述两个版本之间引入的浮点数精度/舍入差异的文档.
Ste*_*ich 13
编辑:感谢Mark Dickinson指出我的初步答案中存在违规行为.由于他的侦探工作,结论发生了变化.非常感谢 ikegami对初步分析的疑虑.
总结:它因为字符串中的小差异而双重对话.看起来这些差异是由32位和64位运行时相同代码的不同行为引起的.
细节
这是为i686-linux-thread-multi-64int构建的perl,v5.8.7
这是用于32位架构的Perl
这是为x86_64-linux构建的perl 5,版本16,subversion 3(v5.16.3)
这适用于64位架构.
这意味着这些Perl版本是针对不同的CPU架构构建的,可能是不同的编译时选项.这可能会导致浮点运算的精度不同.但正如池上的评论所指出的那样,它也可能与字符串到双重对话有关.
对于架构之间的差异,请参阅 维基百科上从i386移至x86_64或x87 FPU与SSE2 时的浮点精度问题.
我在同一台计算机上使用相同版本的Ubuntu(15.10)在LXC容器内完成了以下测试,但一个用于32位,另一个用于64位.
# on 32 bit bit
$ perl -v
This is perl 5, version 20, subversion 2 (v5.20.2) built for i686-linux-gnu-thread-multi-64int
$ perl -V:nvsize
$ nvsize='8';
$ perl -E 'say 2.198696207-2.134326286'
0.0643699209999999
# on 64 bit
$ perl -v
This is perl 5, version 20, subversion 2 (v5.20.2) built for x86_64-linux-gnu-thread-multi
$ perl -V:nvsize
$ nvsize='8';
$ perl -E 'say 2.198696207-2.134326286'
0.0643699210000004
Run Code Online (Sandbox Code Playgroud)
这表明差异与Perl版本或使用的浮点大小无关.为了获得更多细节,我们先看一下使用数字的内部表示unpack('H*',pack('N',$double)).对于2.134326286,表示是相同的,即0xb7e7eaa819130140.但对于2.198696207,我们得到了不同的表示:
32 bit: 2.198696207 -> 0xe*5*3b7709ee960140
64 bit: 2.198696207 -> 0xe*6*3b7709ee960140
Run Code Online (Sandbox Code Playgroud)
这意味着数字的内部表示在64位和32位上是不同的.这可能是由于使用了不同的函数,因为对不同平台进行了优化,或者因为相同的函数在32位和64位上表现略有不同.使用libc函数检查atof显示它也会在64位上返回0xe53b7709ee960140,因此看起来Perl正在使用不同的函数进行对话.
深入挖掘表明我在两个平台上使用的Perl都USE_PERL_ATOF设置了表明Perl正在使用它自己的atof函数实现.可以在此处找到此函数的某些当前实现的源代码.
看看这段代码,很难看出32位和64位的表现如何.但是有一个重要的平台相关值,它表示在将无符号整数添加到浮点的内部表示之前,实现将在unsigned int中累积多少数据:
#define MAX_ACCUMULATE ( (UV) ((UV_MAX - 9)/10))
Run Code Online (Sandbox Code Playgroud)
显然UV_MAX在32位和64位上是不同的,因此它将在32位中引起不同的累加步骤,这导致具有潜在精度问题的不同浮点添加.我的猜测是,它以某种方式解释了32位和64位之间行为的微小差异.