Perl 5.16.3和5.8.7中的浮点精度差异
使用不同版本的perl运行时,下面的代码给出了不同的输出:Perl 5.16.3和5.8.7中的浮点精度差异,perl,floating-point,Perl,Floating Point,使用不同版本的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 = $n
#!/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";
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
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
我找不到任何说明上述两个版本之间引入的浮点数精度/舍入差异的文档。编辑:感谢您指出我最初回答中的不规则之处。由于他的侦查工作,结论改变了。也非常感谢他对最初分析的怀疑
总之:这是因为字符串之间的细微差异导致了双重对话。看起来这些差异是由相同代码在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体系结构和可能不同的编译时选项构建的。这可能会导致浮点运算的精度不同但它也可能与字符串到双重对话有关,正如来自的评论所指出的那样
有关架构之间的差异,请参阅或
我在同一台计算机上用LXC容器中相同版本的Ubuntu(15.10)做了以下测试,但一个是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
这表明差异与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
这意味着数字的内部表示在64位和32位上是不同的。这可能是由于使用了不同的函数,因为针对不同平台进行了优化,或者因为相同的函数在32位和64位上的行为略有不同。使用libc函数atof
进行检查表明,该函数也返回64位的0xe53b7709ee960140,因此看起来Perl正在使用另一个函数进行对话
深入挖掘表明,我在两个平台上使用的Perl都有USE\u Perl\u ATOF
集,这表明Perl正在使用自己的ATOF
函数实现。可以找到此函数的某些当前实现的源代码
看看这段代码,很难看出它在32位和64位的情况下会有什么不同。但有一个重要的平台相关值,它指示在将无符号整数添加到浮点的内部表示形式之前,atof实现将在无符号整数中累积多少数据:
#define MAX_ACCUMULATE ( (UV) ((UV_MAX - 9)/10))
显然,
UV_MAX
在32位和64位上是不同的,因此它将导致32位中的不同累积步骤,从而导致不同的浮点加法,并带来潜在的精度问题。我的猜测是,这在某种程度上解释了32位和64位之间行为上的微小差异。问题当然是每个Perl安装都是为使用浮点体系结构而构建的。但你真的需要这些值是相同的吗?如果是这样,那么你注定会失望不已
单精度(32位)浮点数的精度通常为7位十进制数字,因此您的程序显示的精度远远超过该限制
除非您试图比较两个浮点值是否相等(即使在同一个指令集中,这也是不可能的),否则您可能遇到的唯一问题是,没有一个值具有足够的精度
0.064369920999999
等于0.0643699210000004
,精度为七位,这是您从任何计算机或语言中所能期望的全部您需要的文档是:
Perl可以用三种不同的方式在内部表示数字:本机整数、本机浮点数和十进制字符串。十进制字符串可能具有指数符号部分,如“12.34e-56”中所示。Native在这里的意思是“一种由用于构建perl的C编译器支持的格式”
这取决于您的C编译器和编译时选项
但是,您不必使用本机号码。如果您能够容忍性能下降,您可以使用来获得准确的数字。有一些因素可以产生影响。为了增加这种特殊情况下的可能性,它们如下:
- 这两个版本可能具有不同的浮点指针数大小
- 如果
给出perl-V:nvsize
,则该构建使用双精度浮点指针数字8
- 如果
给出perl-V:nvsize
,则该构建使用四重精度浮点指针数16
- 如果
- C库用于解析数字和格式化数字。由于体系结构的不同,这两个版本使用不同的C库。(由于使用不同的编译器供应商、安装的库版本等,他们也可以使用不同的C库)。有些库在进行这些转换方面比其他库做得更好(即有些库有bug)
- 所使用的指令集(x87 FPU与SSE2)可能因体系结构而异,这很重要,因为它们在内部使用不同的重要性执行操作。有关更多详细信息,请参阅
perl-V:nvsi的输出