2016-12-24 46 views
10

這裏是一個Perl腳本:爲什麼Perl與sprintf舍入不一致?

#!/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結尾爲0.26這是0.255真實的,但它是數100.255

這裏假是Perl的食譜http://docstore.mik.ua/orelly/perl/cookbook/ch02_04.htm

sprintf的報價。 f格式可讓您指定特定數量的小數位數 以將其參數舍入爲。 Perl會查看下面的數字,如果它是5或更大,則 會舍入,否則將舍入。

但我不能看到任何證據證明它是正確的http://perldoc.perl.org/functions/sprintf.html

它是一個在錯誤的sprintf或Perl食譜是錯的?如果這是理想的行爲,爲什麼它以這種方式工作?

+0

似乎也會影響'%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 –

+0

您正在使用兩個舍入數字。相反,使用'說sprintf'%1 $ .19g =>%1 $ .2f',$ number;'。這將回答你的問題。 – ikegami

+0

Re * * Perl會查看下面的數字,如果它是5或更大,則向上舍入,否則捨棄。*「,這是不準確的。這完全取決於底層C庫,它可能不一定使用該舍入方法。事實上,在我的gcc構建中,它甚至達到了。(但這與您的問題無關。) – ikegami

回答

7

Perl使用底層C庫進行格式化。這個庫的功能可能因平臺而異。即使POSIX說「低位數字應以實現定義的方式進行四捨五入」。

在glibc的,這可以說是用來受到廣大perl的二進制文件在那裏,你看到的行爲將幾件事情受到影響:

首先,作爲另一個答案指出,值你認爲四捨五入可能不是完全可以用浮點數來表示,舍入去的方式將取決於下一個更高或更低的可表示數。第二,即使該值可以精確地表示爲兩次可能的舍入之間的中間值,glibc將使用銀行家的舍入。也就是說,它會舍入到一個偶數位。所以sprintf '%.1g', .25會產生.2,但sprintf '%.1g', .75會產生.8

來自Perl Cookbook的引用顯然是錯誤的。

+0

我不會說這本書顯然是錯誤的。它說*「Perl的正常打印程序顯示的數字是以小數點後15位」*。因此,這些數字顯示爲四捨五入正確,但沒有提及銀行家的四捨五入,並且舍入可能與實施有關。 –

+1

@HåkonHægland我的意思是'Perl看着下面的數字,如果它是5或更大,就會向上滾動,否則向下滾動。 – ysth

17

如果添加此行:

$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這是由於浮點數的表示。

+4

更多詳細信息可以在Perl Cookbook的[Chapter 2.3](http://docstore.mik.ua/orelly/perl4/cook/ch02_04.htm)中找到:* 「Perl的正常打印程序顯示四捨五入至小數點後15位左右的數字,但其數字測試不會循環。」* –

+0

此答案不正確。 – ysth