我在其他地方看过,但是真的忘记了答案,所以我再次在这里问。无论你用任何语言编写它,我都不会结束这个循环(我用C#,C ++,Java测试它):
double d = 2.0;
while(d != 0.0){
d = d - 0.2;
}
答案 0 :(得分:32)
浮点计算并不完全精确。您将得到一个表示错误,因为0.2没有精确表示为二进制浮点数,因此该值不会完全等于零。尝试添加调试语句以查看问题:
double d = 2.0;
while (d != 0.0)
{
Console.WriteLine(d);
d = d - 0.2;
}
2 1,8 1,6 1,4 1,2 1 0,8 0,6 0,4 0,2 2,77555756156289E-16 // Not exactly zero!! -0,2 -0,4
解决问题的一种方法是使用decimal
类型。
答案 1 :(得分:27)
(首先,你并没有使用相同的变量,但我认为这是一个错字:)
0.2不是0.2。它是最接近double
的值0.2。当您从2.0中减去10次后,您将不会最终完全 0.0。
在C#中,您可以更改为使用decimal
类型,这将起作用:
// Works
decimal d = 2.0m;
while (d != 0.0m) {
d = d - 0.2m;
}
这是有效的,因为十进制类型 表示精确到0.2的十进制值(在限制范围内;它是128位类型)。所涉及的每一个价值都是精确可表现的,因此它起作用不会工作的是:
decimal d = 2.0m;
while (d != 0.0m) {
d = d - 1m/3m;
}
在这里,“第三个”并不完全可以表示,所以我们最终遇到了和以前一样的问题。
一般来说,在浮点数之间进行精确的相等比较是个坏主意 - 通常你会在一定的容差范围内对它们进行比较。
我在C#/ .NET上下文中有关于floating binary point和floating decimal point的文章,它们更详细地解释了这些内容。
答案 2 :(得分:11)
我记得买过Sinclair ZX-81,通过优秀的Basic编程手册,当我遇到第一个浮点舍入错误时,几乎回到了商店。
我从来没有想到人们在27。99998年后仍会遇到这些问题。
答案 3 :(得分:3)
你最好使用
while(f > 0.0)
*编辑:请参阅下面的pascal评论。但是如果你确实需要运行循环一个完整的,确定的次数,而是使用整数数据类型。
答案 4 :(得分:2)
问题是浮点运算。如果一个数字没有确切的二进制表示,那么你只能存储最接近的数字(就像你不能将数字1/3
存储在十进制中一样 - 你只能存储类似0.33333333
的内容对于某些长度的'3'。这意味着对浮点数的算术通常不完全准确。尝试类似下面的内容(Java):
public class Looping {
public static void main(String[] args) {
double d = 2.0;
while(d != 0.0 && d >= 0.0) {
System.out.println(d);
d = d - 0.2;
}
}
}
您的输出应该是:
2.0
1.8
1.6
1.4000000000000001
1.2000000000000002
1.0000000000000002
0.8000000000000003
0.6000000000000003
0.4000000000000003
0.2000000000000003
2.7755575615628914E-16
现在你应该能够看到为什么条件d == 0
永远不会发生。 (最后一个数字是非常接近0但不完全的数字。
关于浮点怪异的另一个例子,试试这个:
public class Squaring{
public static void main(String[] args) {
double d = 0.1;
System.out.println(d*d);
}
}
因为没有完全0.1
的二进制表示,所以平方它不会产生您期望的结果(0.01
),但实际上类似于0.010000000000000002
!
答案 5 :(得分:1)
f未初始化;)
如果你的意思是:
double f = 2.0;
这可能是非精确关节炎对双重变量的影响。
答案 6 :(得分:1)
这是因为浮点的精度。使用while(d> 0.0),或者如果必须,
while (Math.abs(d-0.0) > some_small_value){
}
答案 7 :(得分:0)
它不会停止,因为0.2我没有用二进制补码精确表示
所以你的循环永远不会执行0.0==0.0
测试
答案 8 :(得分:0)
正如其他人所说,这只是在对任何基础进行浮点运算时所遇到的一个基本问题。只是发生了base-2是计算机中最常见的一个(因为它承认有效的硬件实现)。
如果可能的话,最好的修复方法是切换到使用循环的数字的某种商表示,使得浮点值从中派生。好吧,这听起来太夸张了!对于您的具体情况,我将其写为:
int dTimes10 = 20;
double d;
while(dTimes10 != 0) {
dTimes10 -= 2;
d = dTimes10 / 10.0;
}
在这里,我们真正使用了分数[20 / 10,18 / 10,16 / 10,...,2 / 10,0 / 0],其中迭代是用整数完成的(即,很容易获得在转换为浮点数之前,在具有固定分母的分子中。如果你可以重写你的真实的迭代来做这样的工作,那么你将获得巨大的成功(并且它们实际上并不比你之前所做的那么昂贵,这是一个很好的权衡为了得到正确性。)
如果你不能这样做,你需要使用equ-in-epsilon作为比较。大约是,用d != target
代替abs(d - target) < ε
,其中ε(epsilon)选择有时会很尴尬。基本上,ε的正确值取决于一系列因素,但考虑到步长值的规模,它可能最好选择为0.001作为示例迭代(即,它是步长值的0.5%,所以在其中的任何内容将是错误而不是提供信息。)