0

问题:我想将一个数字向下舍入到最接近的 0.01。

这是我想出的解决方案:

return int($_[0]*100)/100;

似乎很好,直到我偶然在调试器中隔离了这个小宝石:

DB<2> p $_[0]
16.78
DB<3> p $_[0] * 100
1678
DB<4> p int($_[0] * 100)
1677
DB<5> p int(16.78 * 100)
1678

一个重要的注意事项是 $_[0] 最初是从一些文本中删除的。似乎 Perl 将文本“16.78”表示为 16.78 减去一些 epsilon 的数字,但将其打印为“16.78”。

PS POSIX::floor 以同样的方式失败。

到西班牙:

啊,但是如果输入是 16.7799 呢?

use strict;
use warnings;

my $float = 16.7799;
my $not_rounded     = int($float*100)/100;
my $sprintf_rounded = sprintf "%.2f", $float;
my $int_rounded     = int( ($float+0.005) * 100 )/100;

print "first method: " . $not_rounded ."\n";     # => 16.77
print "second method: " . $sprintf_rounded ."\n"; # => 16.78
print "third method: " . $int_rounded . "\n";     # => 16.78

结果应该是 16.77,但在这里它们是:

$ perl test.pl
first method: 16.77
second method: 16.78
third method: 16.78

所以,换个说法,我想向下取整到最接近的百分之一,但在输入接近整数倍的情况下向上取整,可以用浮点数表示,但不超过百分之一。

例如,如果输入是 16.77999999999999758415,这既是 0.01 (16.78) 的最接近倍数的最准确表示,也小于该数字,则向上舍入到最接近的 0.01,否则向下舍入到最接近的 0.01。

请注意,我的操作假设是当 Perl 设置从可以解释为浮点小数的文本中刮取的标量时,它将选择最准确的表示形式。大概是合理的。

4

4 回答 4

2

如前所述,使用浮点运算时必须考虑奇怪的伪影。这里有几种方法可以代替应该起作用的四舍五入:

use strict;
use warnings;

my $float = 16.77999999999999758415
my $not_rounded     = int($float*100)/100;
my $sprintf_rounded = sprintf "%.2f", $float;
my $int_rounded     = int( ($float+0.005) * 100 )/100;

say $not_rounded;     # => 16.77
say $sprintf_rounded; # => 16.78
say $int_rounded;     # => 16.78
于 2013-07-17T17:51:20.083 回答
1

您可以通过在二进制中打印一些不合理的东西来了解内部浮点表示的准确性,就像这样

printf "%.30f\n", 4/9;

我得到

0.444444444444444420000000000000

所以我的浮点数精确到大约十六位有效数字。

现在您可以通过在第 16 位数字上加上 5 来四舍五入到最接近的正确表示的数字,因此允许该点之前的两位数字,

sub trunc {
  my $x = shift() + 5E-14;
  int($x * 100) / 100.0;
}

print trunc(16.77999999999999758415);

输出

16.78

当然,没有必要突破 Perl 浮点数的准确性限制:您可以简单地将输入数据准确性的一半相加并获得正确的结果。

于 2013-07-17T20:36:46.687 回答
1
    #!usr/bin/perl -w
    use strict;
    $\ = "\n";
    print "Enter the value and the roundto [Ex:roundto nearest 100 or 1000 etc] ";
    chomp (my $value = <STDIN>);
    chomp (my $digit = <STDIN>);
    my $round;
    $round = int( ($value+ $digit /2)/ $digit )* $digit;
    print "After rounding: $round";
于 2013-07-17T22:12:15.563 回答
0

这是一个解决方案。肯定有高人一等。

sub regex_round_down {
    my $var = shift;
    $var =~ s/^\./0./;
    $var =~ s/^(\d+(\.\d{1,2})?).*$/$1/;
    return $var;
}
于 2013-07-17T19:28:51.080 回答