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

时间:2016-12-24 11:37:27

标签: 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 $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 http://docstore.mik.ua/orelly/perl/cookbook/ch02_04.htm

的引用
  sprintf。 f格式允许您指定特定的小数   把它的论点四舍五入到的地方。 Perl看下面的数字,   如果它是5或更大,则向上舍入,否则向下舍入。

但我在http://perldoc.perl.org/functions/sprintf.html

中看不到任何证据证明它是正确的

是sprintf中的错误还是Perl Cookbook错误?如果它是理想的行为,为什么它会这样工作?

2 个答案:

答案 0 :(得分:18)

如果你添加这一行:

$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,这是由于浮点数的表示。

答案 1 :(得分:7)

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

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

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

其次,即使该值在两次可能的舍入之间可以准确表示,glibc也会使用银行家的舍入。也就是说,它会四舍五入到偶数位。因此sprintf '%.1g', .25将生成.2,但sprintf '%.1g', .75将生成.8

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