为什么Perl与sprintf舍入不一致?

为什么Perl与sprintf舍入不一致?,perl,floating-point,printf,Perl,Floating Point,Printf,下面是一个Perl脚本: #!/usr/bin/perl use strict; use warnings; use feature 'say'; my @numbers = qw( 0.254 0.255 0.256 ); foreach my $number (@numbers) { my $rounded = sprintf '%.2f', $number; say "$number => $rounded"; } foreach my

下面是一个Perl脚本:

#!/usr/bin/perl

use strict;
use warnings;
use feature 'say';

my @numbers = qw(
    0.254
    0.255
    0.256
);

foreach my $number (@numbers) {
    my $rounded = sprintf '%.2f', $number;
    say "$number => $rounded";
}

foreach my $number (@numbers) {
    $number += 100;
    my $rounded = sprintf '%.2f', $number;
    say "$number => $rounded";
}
它输出:

0.254 => 0.25
0.255 => 0.26
0.256 => 0.26
100.254 => 100.25
100.255 => 100.25
100.256 => 100.26
对我来说,Perl与舍入不一致是非常奇怪的。我希望以.255结尾的两个数字都四舍五入为.26。对于0.255是正确的,但是对于100.255则是错误的

下面是Perl Cookbook中的一段话

斯普林特。f格式允许您指定特定的十进制数 使其论点更具说服力的地方。Perl查看以下数字, 如果大于等于5,则向上舍入,否则向下舍入

但我看不到任何证据表明这是正确的

这是sprintf中的错误还是Perl Cookbook中的错误?如果这是需要的行为,为什么会这样做?

如果您添加这一行:

$number = sprintf '%.15f', $number;
打印前,您将有:

0.254000000000000 => 0.25
0.255000000000000 => 0.26
0.256000000000000 => 0.26
100.254000000000005 => 100.25
100.254999999999995 => 100.25
100.256000000000000 => 100.26

如您所见,
100.255
并不完全是
100.255
这是由于浮点数的表示。

Perl使用底层C库进行格式化。此库的功能可能因平台而异。甚至POSIX也表示“低阶数字应以实现定义的方式四舍五入。”

在glibc中,大多数perl二进制文件都使用glibc,您看到的行为将受到以下几点的影响:

首先,正如在另一个答案中指出的那样,您认为要舍入的值可能无法用浮点表示,舍入的方式将取决于它是下一个更高或更低的表示数

其次,即使该值正好表示为两个可能舍入的中间值,glibc也将使用银行家舍入法。也就是说,它将四舍五入到一个偶数位数。因此
sprintf'.1g',.25
将产生
.2
,但是
sprintf'.1g',.75
将产生
.8


《Perl食谱》中的引文完全是错误的。

似乎也会影响
%g
Perl-E'printf'%.3g\n',1.505'
给出1.5,而
Perl-E'printf'%.3g\n',1.506'
给出1.51。此外,
perl-E'printf'%.2g\n',0.505'
perl-E'printf'%.2g\n',0.506'
都给出了0.51个四舍五入的数字。相反,使用
表示sprintf“%1$.19g=>%1$.2f',$number。这将回答您的问题。Re“Perl查看以下数字,如果是5或更大,则向上舍入,否则向下舍入。”,这是不准确的。这完全取决于底层的C库,它可能不一定使用这种舍入方法。事实上,在我的gcc构建中,它甚至达到了平衡。(但这与您的问题无关)更多详细信息可以在Perl Cookbook中找到:“Perl的正常打印例程显示的数字四舍五入到小数点后15位左右,但它的数字测试不四舍五入。”我不会说这本书完全错了。它说“Perl的正常打印例程显示数字,四舍五入到15位小数”。因此,数字显示为四舍五入是正确的,但它没有提到银行家的四舍五入,而且四舍五入可能取决于实现。@HåkonHægland我的意思是
Perl查看以下数字,如果是5或更大,则向上取整,否则向下取整。