Perl舍入printf / sprintf的bug

时间:2014-02-12 10:23:54

标签: perl printf rounding-error

任何人都可以解释这个:

#!/usr/bin/perl -w
my $amount = 74.32 * 100.00;

printf "Unformatted Amount:${amount}:\n";
printf " Formatted Amount:%.11d:\n", $amount;

输出:

$ ./test.pl
Unformatted Amount:7432:
  Formatted Amount:00000007431:

目前我已停止使用printf和sprintf,因为我不相信它们。所以我这样做了:

#!/usr/bin/perl -w
my $amount = 74.32 * 100.00;

$formatted_amount = "00000000000" . $amount;
$formatted_amount = substr($formated_amount,length($formatted_amount)-11,11);

printf "Unformatted Amount:${amount}:\n";
printf "  Formatted Amount:${formatted_amount}:\n";

虽然有效但我很想知道为什么我需要这样做。

我理解二进制数字如何不能完全代表所有基数为10的数字,并且已经阅读了这个Stack Overflow条目:How to eliminate Perl rounding errors,但老实说我没想到看到这个看似简单的例子有问题。

$ amount 的值也是正确的。这只是printf无法正确打印!

有什么想法吗?

1 个答案:

答案 0 :(得分:4)

我认为printf应该放弃

您希望%.11d的说明符做什么?

小数点后的值精度,而perldoc -f sprintf表示

  

对于整数转换,指定精度意味着                   数字本身的输出应为零填充                   这个宽度,忽略0标志

如果你想要一个11个字符的最小宽度而不是省略小数点 - %11d会给你你想要的。

Perl通过截断将浮点数转换为整数,并且74.32 * 100.0 略小于而不是7432,int(74.32 * 100.0)为7431。您希望浮点值四舍五入到最接近的整数,然后使用f格式说明符,其精度为零,如%11.0f

要填充字段,请查看上述文档的标记部分。标志字符出现在宽度说明符之前:0 use zeros, not spaces, to right-justify

检查此程序

use strict;
use warnings;

my $amount = 74.32 * 100.00;

printf "Unformatted Amount:    ${amount}\n";
printf "Truncated Amount:      %d\n", $amount;
printf "High-precision Amount: %20.16f\n", $amount;
printf "Rounded Amount:        %.0f\n", $amount;
printf "Padded Amount:         %011.0f\n", $amount;

<强>输出

Unformatted Amount:    7432
Truncated Amount:      7431
High-precision Amount: 7431.9999999999991000
Formatted Amount:      7432
Padded Amount:         00000007432