Perl Int函数没有正确转换,减少

时间:2013-07-17 17:19:16

标签: perl casting int

问题:我想将数字向下舍入到最近的.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”。

P.S。 POSIX :: floor以同样的方式失败。

对于dspain:

啊,但如果输入是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 个答案:

答案 0 :(得分: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

答案 1 :(得分: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浮点数的精确度限制:您可以简单地添加输入数据准确度的一半,并获得正确的结果。

答案 2 :(得分: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";

答案 3 :(得分:0)

这是解决方案的一次通过。当然有一个优越的。

sub regex_round_down {
    my $var = shift;
    $var =~ s/^\./0./;
    $var =~ s/^(\d+(\.\d{1,2})?).*$/$1/;
    return $var;
}