我不能为我的生活弄清楚为什么以下产生它的结果。
use POSIX;
my $g = 6.65;
my $t = $g * 4;
my $r = $t - $g;
my $n = $r / $g;
my $c = ceil($n);
print "$c ($n)\n";
Sigil-tastic,我知道 - 抱歉。
我已经为我的应用解决了这个问题,如下所示:
use POSIX;
my $g = 6.65;
my $t = $g * 4;
my $r = $t - $g;
my $n = $r / $g;
my $c = ceil("$n");
print "$c ($n)\n";
...但我很困惑为什么这里有必要。
答案 0 :(得分:8)
这是怎么回事:$n
包含一个浮点值,因此不完全等于3
,在我的计算机上它是3.00000000000000044409
。 Perl非常聪明,可以在打印时将其舍入到3
,但是当您明确使用浮点函数时,它将完全按照它所宣传的内容执行:ceil
到下一个整数:{{1} }}
这是使用浮点数的现实,绝不是Perl特有的。你不应该依赖他们的确切价值。
答案 1 :(得分:6)
use strict;
use warnings;
use POSIX;
my $g = 6.65;
my $t = $g * 4;
my $r = $t - $g;
my $n = $r / $g; # Should be exactly 3.
# But it's not.
print "Equals 3\n" if $n == 3;
# Check it more closely.
printf "%.18f\n", $n;
# So ceil() is doing the right thing after all.
my $c = ceil($n);
print "g=$g t=$t r=$r n=$n c=$c\n";
答案 2 :(得分:5)
强制性Goldberg参考:What Every Computer Scientist Should Know About Floating-Point Arithmetic。
使用Perl,在数字运算中将字符串视为数字的能力变成了一个优势,因为您可以轻松地使用sprintf
明确指定所需的精度数量:
use strict; use warnings;
use POSIX qw( ceil );
my $g = 6.65;
my $t = $g * 4;
my $r = $t - $g;
my $n = $r / $g;
my $c = ceil( sprintf '%.6f', $n );
print "$c ($n)\n";
输出:
C:\Temp> g 3 (3)
问题出现的原因是,由于可用于表示数字的有限位数,因此只有大量但有限数量的数字可以浮点表示。鉴于实线上有无数的数字,这将导致在存在中间操作时出现近似和舍入误差。
答案 3 :(得分:4)
不是Perl问题,因此
#include <stdlib.h>
#include <math.h>
#include <stdio.h>
main()
{
double n = (6.65 * 4.0 - 6.65) / 6.65;
double c = ceil(n);
printf("c is %g, n was %.18f\n", c, n);
}
c is 4, n was 3.000000000000000444
答案 4 :(得分:4)
某些数字(如6.65)具有十进制的精确表示,但无法在计算机使用的二进制浮点中精确表示(就像1/3没有精确的十进制表示)。因此,浮点数通常与您预期的略有不同。计算结果不是3,而是约3.000000000000000444。
传统的处理方法是定义一些小数字(称为epsilon),如果两个数字相差小于epsilon,则认为它们相等。
您的ceil("$n")
解决方案有效,因为Perl在将其转换为字符串时将浮点数舍入到大约14位小数(因此将3.000000000000000444转换回3)。但更快的解决方案是在计算ceil
之前减去epsilon(因为ceil
将向上舍入):
my $epsilon = 5e-15; # Or whatever small number you feel is appropriate
my $c = ceil($n - $epsilon);
浮点减法应该比转换为字符串并返回更快(这涉及很多除法)。
答案 5 :(得分:1)
其他答案已经解释了问题存在的原因,有两种方法可以让它消失。
如果可以,可以编译Perl以使用更高精度的类型。使用-Duse64bitint -Duselongdouble
进行配置将使Perl使用64位整数和长双精度。它们的精度足以使大多数浮点问题消失。
另一种方法是使用bignum,这将启用透明的任意精度数字支持。这个速度较慢,但很精确,可以在词汇上使用。
{
use bignum;
use POSIX;
my $g = 6.65;
my $t = $g * 4;
my $r = $t - $g;
my $n = $r / $g;
my $c = ceil($n);
print "$c ($n)\n";
}
您还可以使用Math::BigFloat
声明单个任意精度数字