我有这个简单的测试:
double h;
...
// code that assigns h its initial value, used below
...
if ((h>0) && (h<1)){
//branch 1 -some computations
}
else{
//branch 2- no computations
}
我列出了我的值,因为我得到了一些非常奇怪的结果,例如: h = 1然后到达第一个分支,我不明白为什么因为如果h = 1我想要计算branch2 我对这么明显的东西感到困惑吗?
这是我计算然后使用h
的方式:
double* QSweep::findIntersection(edge_t edge1,edge_t edge2) {
point_t p1=myPoints_[edge1[0]];
point_t p2=myPoints_[edge1[1]];
point_t p3=myPoints_[edge2[0]];
point_t p4=myPoints_[edge2[1]];
double xD1,yD1,xD2,yD2,xD3,yD3,xP,yP,h,denom;
double* pt=new double[3];
// calculate differences
xD1=p2[0]-p1[0];
xD2=p4[0]-p3[0];
yD1=p2[1]-p1[1];
yD2=p4[1]-p3[1];
xD3=p1[0]-p3[0];
yD3=p1[1]-p3[1];
xP=-yD1;
yP=xD1;
denom=xD2*(-yD1)+yD2*xD1;
if (denom==0) {
return NULL;
}
else{
h=(xD3*(-yD1)+yD3*xD1)/denom;
}
std::cout<<"h is"<<h<<endl;
if (h < 1) std::cout<<"no"<<endl;
else std::cout<<"yes"<<endl;
if (h==1) {
return NULL;
}
else{
if ((h>0)&&(h<1)){
pt[0]=p3[0]+xD2*h;
pt[1]=p3[1]+yD2*h;
pt[2]=0.00;
}
else{
return NULL;
}
}
return pt;
}
好的,很明显我应该如何重新制定病情。
自:
double h;
if (h==1){
//computations here
}
要:
double h;
if (abs(h-1)<tolerance){
//computations here
}
当我使用双号时。
但我该如何改编呢?
double h;
if (h<1){
//computations here
}
答案 0 :(得分:14)
由于h
是双精度数,因此它可能已接近1以打印为1
,但它实际上小于1,因此比较成功。 Floating-point numbers做了很多。
答案 1 :(得分:5)
通过printing it out with maximum precision检查h的实际值。您可能会发现它实际上略低于1.0。
我将以下代码作为测试运行
#include <iostream>
int main()
{
double h = 1.0;
if((h>0) && (h<1))
{
std::cout << "first branch" << std::endl;
}
else
{
std::cout << "second branch" << std::endl;
}
}
并且输出是“第一个分支”(在Ubuntu 8.10上使用g ++ 4.3.2),但Indeera在评论中提到,在使用VS2005编译的Windows XP上运行的相同代码给出输出“第二个分支”(谢谢,Indeera )。
您可以更改代码,将h
和0.0以及h
和1.0之间的差异与某个较小的delta值进行比较。
double allowedDelta = 0.000001;
if( ((h - 0.0) > allowedDelta) && ((1.0 - h) > allowedDelta) )
... // h is between 0.000001 and 0.9999990
请注意,在这种特殊情况下,“(h - 0.0)”可以替换为“h”。我将其保留为具有说明价值的方式。
另请注意,如果您只进行一次比较,则需要将delta与h与某些常量之差的绝对值进行比较。由于您在这里检查范围,因此两个比较ANDed一起构成了一个特殊情况,您可以绕过abs
的使用。如果h是负值或某个大于1.0的正值,则它将超出范围并且无法通过上述两个测试中的一个。
答案 2 :(得分:4)
简短的故事:您的测试不正确,因为浮点数的行为与您可能期望的不同。特别是像“denom == 0”这样的东西是有问题的。
Sun已经足够好在线提供这篇论文了:
What Every Computer Scientist Should Know About Floating Point Arithmetic
与广告完全一致。根据你的背景,它将是一个简单的阅读或大量的工作,但对于任何使用浮动的程序员来说,它确实值得花时间。
添加评论:我并不是说每个程序员都会轻易理解该论文中的所有内容。阅读它虽然至少可以更好地了解浮动实际上是什么,问题是什么,以及如何正确处理事情的一些提示。
如果你想要正确地进行大量的数字工作,你将不得不阅读各种技术,但这将是一本教科书(或几个)的材料。这里的评论已经指出了一些基础知识,并与更多
相关联答案 3 :(得分:3)
比较浮点值时始终允许舍入错误。而不是测试平等,这样的事情通常是你想要的:
if (abs(x - y) < epsilon) ...
其中epsilon是一个适当的小值,如0.00001。
浮点数学不精确有几个原因。首先,并非所有值都可以准确表示(例如0.1,不能用二进制浮点数表示,与1/3不能用小数表示的方式相同)
另一个是浮点只使用固定数量的有效数字(相对于小数点“浮动”,因此名称)。所以在大数字上,小分数有效地被截断了。永远不要认为浮点计算会返回准确的结果。
答案 4 :(得分:1)
这可能是一个精确的问题。 H可能不完全是1,但非常接近它。你能否发布一些关于你想要做什么的更多信息,例如,H的价值来自哪里,它在哪里?
答案 5 :(得分:0)
这可能与C / C ++中的双精度为64位这一事实有关,但计算可以以更高的精度完成(cpu的浮点寄存器更宽(96位),所以甚至不是像cos(x)== cos(x)可能是真的。
参考:http://www.parashift.com/c++-faq-lite/newbie.html#faq-29.18
答案 6 :(得分:0)
原因是浮点数不是您在变量中保存的数字的真实表示。 (反对BCD [二进制编码小数])
您可以在此处查看定义: Floating Point Wikipedia
所以问题是某些数字不能用给定的一组位来表达。 (如果你可以无限地添加位,你可以) 诀窍在于,在大多数情况下,保存的数字和预期数量之间的差异非常小。在实践中,你有一些可能导致问题的极端情况。 例如,这就是您不应该构建财务软件并使用浮点数进行货币计算的原因。您可以很容易地发现明显的差异,这是税务局不喜欢的。
因此,为了比较浮点数,您应该始终应用某种适用于您的应用程序的阈值。类似的东西:
if(a==b)
变为
if(abs(a-b)<threshold)
编辑:正如大卫在评论中提到的那样,你仍然会遇到像pi,1/3,...等问题。
但是你至少可以存储数字而不会损失你输入系统的精度。由于计算机的内存有限,您可以随时构建不能依赖精确表示的极端情况......
刚看到你对文字的编辑,所以这是下一个编辑:
if(a<1)
在某种程度上更难,因为你不知道它是否仅仅是数字表示是错误的还是它只是一个接近1的实数值。它实际上取决于算法的要求。如果出现小错误,请执行以下操作:
if(a < 1-threshold)
如果不行,那么你必须使用另一个没有遇到问题的变量类型。
答案 7 :(得分:0)
好的,你已经发布了代码。你是通过一系列算术运算来计算h,看起来像是相当任意的数字。这意味着你将非常接近h的理想值,但不是非常接近正确的值。
这意味着您需要进行近似比较。测试(h == 1.0)只会偶然成功;尝试(fabs(h - 1.0)&lt; 1e-10)或类似的东西(使用const double代替公差的幻数)。为其他比较做出适当的更改。
答案 8 :(得分:0)
您可能对GDC 2005上的Numerical Robustness for Geometric Calculations(又名“EPSILON is 0.00001!”)发表的演讲非常感兴趣。
答案 9 :(得分:-1)
如果你必须在支票中使用浮点数,将它们四舍五入并将其存储在例如整数中。 1f可以是1.0000000000000000000000000000000001或0.99999999999999999999999999999999999999