加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

Perl 5.16.3和5.8.7中的浮动精度差异

发布时间:2020-12-16 06:10:40 所属栏目:大数据 来源:网络整理
导读:在使用不同版本的perl运行时,下面的代码片段给出了不同的输出: #!/usr/bin/env perlmy $number1 = 2.198696207;my $number2 = 2.134326286;my $diff = $number1 - $number2;print STDOUT "n 2.198696207 - 2.134326286: $diffn";$number1 = 0.449262271;$
在使用不同版本的perl运行时,下面的代码片段给出了不同的输出:

#!/usr/bin/env perl

my $number1 = 2.198696207;
my $number2 = 2.134326286;
my $diff = $number1 - $number2;
print STDOUT "n 2.198696207 - 2.134326286: $diffn";

$number1 = 0.449262271;
$number2 = 0.401361096;
$diff = $number1 - $number2;
print STDOUT "n 2.198696207 - 2.134326286: $diffn";

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,for GNU/Linux 2.2.5,not stripped
 2.198696207 - 2.134326286: 0.0643699209999999
 2.198696207 - 2.134326286: 0.047901175

我无法找到任何说明上述两个版本之间引入的浮点数精度/舍入差异的文档.

解决方法

编辑:感谢 Mark Dickinson指出我的初步答案中的不规范.由于他的侦探工作,结论发生了变化.非常感谢 ikegami对初步分析的怀疑.

总结:它因为字符串中的小差异而双重对话.看起来这些差异是由32位和64位运行时相同代码的不同行为引起的.

细节

This is perl,v5.8.7 built for i686-linux-thread-multi-64int

这是用于32位架构的Perl

This is perl 5,subversion 3 (v5.16.3) built for x86_64-linux

这适用于64位架构.

这意味着这些Perl版本是针对不同的CPU架构构建的,可能是不同的编译时选项.这可能会导致浮点运算的精度不同.但正如ikegami的评论所指出的那样,它也可能与字符串到双重对话有关.

有关架构之间的差异,请参阅Problem with floating-point precision when moving from i386 to x86_64或 x87 FPU vs. SSE2 on Wikipedia.

我在同一台计算机上使用相同版本的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,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_PERL_ATOF,表明Perl正在使用自己的atof函数实现.可以在here找到该函数的一些当前实现的源代码.

看看这段代码,很难看出32位和64位的表现如何.但是有一个重要的平台相关值,它表示在将无符号整数添加到浮点的内部表示之前,实现将在unsigned int中累积多少数据:

#define MAX_ACCUMULATE ( (UV) ((UV_MAX - 9)/10))

显然,UV_MAX在32位和64位上是不同的,因此它将在32位中引起不同的累加步骤,这导致不同的浮点加法以及潜在的精度问题.我的猜测是,它以某种方式解释了32位和64位之间行为的微小差异.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读