Perl printf/sprintf 的舍入错误

Bar*_*ard 1 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;
Run Code Online (Sandbox Code Playgroud)

输出这个:

$ ./test.pl
Unformatted Amount:7432:
  Formatted Amount:00000007431:
Run Code Online (Sandbox Code Playgroud)

目前我已经停止使用 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";
Run Code Online (Sandbox Code Playgroud)

这有效,但很好奇为什么我需要这样做。

我了解二进制数字如何无法准确表示所有以 10 为基数的数字,并且已阅读此 Stack Overflow 条目:如何消除 Perl 舍入错误,但老实说,我没想到会在如此看似简单的示例中看到问题。

$amount 的值也是的。只是 printf 没有正确打印它!

有任何想法吗?

Bor*_*din 5

我想printf应该放弃了!

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

小数点后的值是精度perldoc -f sprintf表示这个

对于整数转换,指定精度意味着数字本身的输出应该用零填充到此宽度,其中 0 标志被忽略

如果您想要11 个字符的最小宽度,那么只需省略小数点 -%11d就会给您您想要的。

Perl 通过截断浮点数将其转换为整数,稍小于7432 的是 7431。如果您希望浮点值四舍五入到最接近的整数,74.32 * 100.0请使用零精度的格式说明符,例如。int(74.32 * 100.0)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;
Run Code Online (Sandbox Code Playgroud)

输出

Unformatted Amount:    7432
Truncated Amount:      7431
High-precision Amount: 7431.9999999999991000
Formatted Amount:      7432
Padded Amount:         00000007432
Run Code Online (Sandbox Code Playgroud)