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

bes*_*bov 11 floating-point perl 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 $number (@numbers) {
    $number += 100;
    my $rounded = sprintf '%.2f', $number;
    say "$number => $rounded";
}
Run Code Online (Sandbox Code Playgroud)

它输出:

0.254 => 0.25
0.255 => 0.26
0.256 => 0.26
100.254 => 100.25
100.255 => 100.25
100.256 => 100.26
Run Code Online (Sandbox Code Playgroud)

对我来说,Perl与舍入不一致是很奇怪的.我希望以.255结尾的两个数字都被舍入为.26这对于0.255是正确的,但对于数字100.255它是错误的

以下是Perl Cookbook的报价http://docstore.mik.ua/orelly/perl/cookbook/ch02_04.htm

sprintf.f格式允许您指定特定数量的小数位以将其参数舍入到.Perl查看下面的数字,如果它是5或更大,则向上舍入,否则向下舍入.

但我在http://perldoc.perl.org/functions/sprintf.html中看不到任何证据证明它是正确的

这是sprintf或Perl Cookbook中的错误吗?如果需要行为,为什么它会这样工作?

Tot*_*oto 18

如果你添加这一行:

$number = sprintf '%.15f', $number;
Run Code Online (Sandbox Code Playgroud)

在打印之前,您将拥有:

0.254000000000000 => 0.25
0.255000000000000 => 0.26
0.256000000000000 => 0.26
100.254000000000005 => 100.25
100.254999999999995 => 100.25
100.256000000000000 => 100.26
Run Code Online (Sandbox Code Playgroud)

正如你所看到的,100.255是不完全的100.255,这是由于浮点数的表示.

  • 更多细节可以在Perl Cookbook的[第2.3章](http://docstore.mik.ua/orelly/perl4/cook/ch02_04.htm)中找到:*"Perl的正常打印例程显示的数字四舍五入到小数点后15位左右,但它的数字测试不圆."* (4认同)

yst*_*sth 7

Perl使用底层C库进行格式化.这个库的功能可能因平台而异.甚至POSIX也说"低阶数字应以实现定义的方式进行舍入."

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

首先,正如另一个答案中所指出的,您认为正在舍入的值可能无法在浮点中精确表示,并且舍入的方式将取决于它是否是下一个更高或更低的可表示数字.

其次,即使价值在两次可能的舍入之间完全可以表示,glibc也会使用银行家的舍入.也就是说,它会四舍五入到偶数位.所以sprintf '%.1g', .25会产生.2,但sprintf '%.1g', .75会产生.8.

Perl Cookbook的引用是完全错误的.