问题:我想将数字向下舍入到最近的.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设置一个可以解释为浮点十进制的文本中的标量时,它会选择最精确的表示。可能是合理的。
答案 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;
}