浮点比较奇怪的结果

时间:2022-12-27 04:04:58

I have this simple test:

我有这个简单的测试:

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
}

I listed my values as I got some really strange results and for example if: h=1 then the first branch is reached and I do not understand why since if h=1 I want branch2 to be computed.
Am I getting confused by something so obvious?

我列出了我的值,因为我得到了一些非常奇怪的结果,例如:h = 1然后第一个分支到达,我不明白为什么因为如果h = 1我想要计算branch2。我被这么明显的东西搞糊涂了吗?


Edit:

This is how I compute and then use h:

这是我计算然后使用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;  

}


Edit:

Okay, so it is clear how I should reformulate the condition.

好的,很明显我应该如何重新制定这个条件。

From:

double h;
if (h==1){
   //computations here
}

To:

double h;
if (abs(h-1)<tolerance){
  //computations here
}

When I use double numbers.

当我使用双号。

But how do I reformulate this?

但是我如何重新制定这个呢?

double h;
if (h<1){
   //computations here
}

10 个解决方案

#1


14  

Since h is a double, it may have been close enough to 1 to print as 1, but it is actually a bit less than 1 so the comparison succeeds. Floating-point numbers do that a lot.

由于h是一个double,它可能已经足够接近1打印为1,但它实际上小于1,因此比较成功。浮点数可以做很多事情。

#2


5  

Check the actual value of h by printing it out with maximum precision. You'll probably find that it is actually slightly less than 1.0.

通过最大精度打印来检查h的实际值。您可能会发现它实际上略小于1.0。

I ran the following code as a test

我运行以下代码作为测试

#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;
    }
}

and the output was "first branch" (using g++ 4.3.2 on Ubuntu 8.10), but Indeera mentioned in a comment that the same code running on Windows XP compiled with VS2005 gives the output "second branch" (thanks, Indeera).

输出是“第一个分支”(在Ubuntu 8.10上使用g ++ 4.3.2),但Indeera在评论中提到,在使用VS2005编译的Windows XP上运行的相同代码给出输出“第二个分支”(谢谢,Indeera)。

You might change your code to compare the differences between h and 0.0 and h and 1.0 to some small delta value.

您可以更改代码以将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

Note that "(h - 0.0)" can be replaced with "h" in this special case. I'm leaving it the way it is for illustrative value.

请注意,在这种特殊情况下,“(h - 0.0)”可以替换为“h”。我将它留给说明价值的方式。

Also note that if you were only making one comparison you'd need to compare the delta to the absolute value of the difference between h and some constant. Since you're checking a range here, the two comparisons ANDed together make a special case where you can bypass the use of abs. If h is a negative value or some positive value greater than 1.0 it will be out of range and fail one of the two tests above.

另请注意,如果您只进行一次比较,则需要将delta与h与某些常量之差的绝对值进行比较。由于你在这里检查一个范围,两个比较ANDed一起构成一个特殊情况,你可以绕过abs的使用。如果h是负值或某个大于1.0的正值,则它将超出范围并且无法通过上述两个测试之一。

#3


4  

Short story: Your tests are incorrect because floating point numbers do not behave like you probably expect them to. Particularly things like "denom == 0" are problematic.

简短的故事:您的测试不正确,因为浮点数的行为与您可能期望的不同。特别是像“denom == 0”这样的东西是有问题的。

Sun has been nice enough to provide this paper online:

Sun已经足够好,可以在线提供这篇论文:

What Every Computer Scientist Should Know About Floating Point Arithmetic

每个计算机科学家应该知道的浮点运算

which is exactly as advertised. Depending on your background it will be an easy read or a big of work, but it really is worth the time for any programmer using floats.

这与宣传的一模一样。根据您的背景,它将是一个简单的阅读或大量的工作,但它确实值得花时间任何程序员使用浮动。

ADDED COMMENT: I'm not suggesting that every programmer will easily understand everything in that paper. Reading over it though will at the very least give a better idea of what floats actually are, what the issues are, and some hints on how to handle things properly.

添加评论:我并不是说每个程序员都会轻易理解该论文中的所有内容。阅读它虽然至少可以更好地了解浮动实际是什么,问题是什么,以及如何正确处理事情的一些提示。

If you want to do a lot of numerical work properly, you're going to have to read up on the various techniques, but that would be a textbook (or several) worth of material. The comments here have already pointed out some of the basics, and linked to more

如果你想要做很多正确的数值工作,你将不得不阅读各种技术,但这将是一本教科书(或几个)的材料。这里的评论已经指出了一些基础知识,并与更多内容相关联

#4


3  

Always allow for rounding errors when comparing floating point values. Rather than testing for equality, something like this is usually what you want:

在比较浮点值时始终允许舍入错误。而不是测试平等,这样的事情通常是你想要的:

if (abs(x - y) < epsilon) ...

where epsilon is some suitably small value, like 0.00001.

其中epsilon是一些适当的小值,如0.00001。

There are several reasons why floating point math isn't accurate. First, not all values can be represented exactly (0.1 for example, can not be represented in binary floating point numbers, in the same way that 1/3 can't be represented in decimals)

浮点数学不准确的原因有几个。首先,并非所有值都可以准确表示(例如,0.1不能用二进制浮点数表示,与1/3不能用小数表示的方式相同)

Another is that floating point only uses a fixed number of significant digits (which "float" relative to the decimal point, hence the name). So on large numbers, small fractions effectively get truncated away. Never assume that floating point computations return an accurate result.

另一个是浮点只使用固定数量的有效数字(相对于小数点“浮动”,因此名称)。因此,在大数字上,小分数有效地被截断。永远不要假设浮点计算返回准确的结果。

#5


1  

It might be a precision issue. H might not be exactly 1, but something very near to it. Could you post some more information on what you're trying to do, for instance, where does the value of H come from, and where does it go?

这可能是一个精确的问题。 H可能不完全是1,但非常接近它。你能否发布一些关于你想要做什么的更多信息,例如,H的价值来自何处,它在哪里?

#6


0  

It could have to do with the fact that doubles in C/C++ are 64bit, but computation could be done at higher precision (the floating point registers of your cpu are wider (96 bit), so that not even a statement like cos(x)==cos(x) could be true.

它可能与C / C ++中的双精度为64位这一事实有关,但计算可以以更高的精度完成(cpu的浮点寄存器更宽(96位),因此甚至不像cos(x)这样的语句)== cos(x)可能是真的。

Reference: http://www.parashift.com/c++-faq-lite/newbie.html#faq-29.18

#7


0  

Okay, you've posted the code. You are calculating h by a series of arithmetic operations from what looks like fairly arbitrary numbers. This means you're going to get a very close approximation to the ideal value of h, but not quite the right one.

好的,你已经发布了代码。你是通过一系列算术运算来计算h,看起来像是相当任意的数字。这意味着你将获得非常接近h的理想值,但不是非常接近正确值。

This means that you need to do approximate comparisons. Testing (h == 1.0) will succeed only by accident; try (fabs(h - 1.0) < 1e-10) or something like that (using a const double instead of a magic number for tolerance). Make the appropriate changes for other comparisons.

这意味着您需要进行近似比较。测试(h == 1.0)只会偶然成功;尝试(fabs(h - 1.0)<1e-10)或类似的东西(使用const double代替公差的幻数)。对其他比较进行适当的更改。

#8


0  

You may be very interested in the Numerical Robustness for Geometric Calculations (aka "EPSILON is NOT 0.00001!") talk given at GDC 2005.

您可能对GDC 2005中给出的几何计算的数值稳健性(又名“EPSILON is 0.00001!”)非常感兴趣。

#9


0  

The reason is that floating point numbers are not a real representation of the number that you save in the variable. (Opposed to BCD [binary coded decimals])

原因是浮点数不是您在变量中保存的数字的真实表示。 (反对BCD [二进制编码小数])

You can see the definition here: Floating Point Wikipedia

你可以在这里看到定义:浮点*

So the problem is that certain numbers are not expressable with a given set of bits. (You could if you could add bits infinitely) The trick is that in most cases the difference between the saved number and the intended number are pretty small. In practice, you have some corner cases where this may lead to problems. This is for example the reason why you should not build a financial software and use floating point numbers for money calculations. You can easily have differences that are noticeable, which your tax office won't like.

所以问题是某些数字不能用给定的一组比特来表达。 (如果你可以无限地添加位,你可以这样做)诀窍是,在大多数情况下,保存的数字和预期数量之间的差异非常小。在实践中,你有一些可能导致问题的极端情况。这就是为什么你不应该建立财务软件并使用浮点数进行货币计算的原因。您可以轻松获得明显的差异,税务机关不会喜欢这些差异。

So to compare floating point numbers you should always apply some kind of threshhold that is ok for your application. Something like:

因此,为了比较浮点数,您应该始终应用某种适用于您的应用程序的阈值。就像是:

if(a==b) 

becomes

if(abs(a-b)<threshold)

Edit: As David Mentioned in his comment, you will still get into trouble with things like pi, 1./3., ... But you can at least store numbers without loss in precision that you put into the system. As computers have limited memory you can always construct corner cases where you can't rely on exact representations...

编辑:正如David在他的评论中提到的那样,你仍然会遇到pi,1/3等问题。但是你至少可以存储数字,而不会丢失你输入系统的精确度。由于计算机的内存有限,您可以随时构建不能依赖精确表示的极端情况......

Just saw your edit of the text, so here is the next edit:

刚刚看到你对文本的编辑,所以这是下一个编辑:

if(a<1)

Is somehow harder, because you don't know whether it's just the number representation that is wrong or if it's just a real value that is close to 1. It really depends on the requirements for your algorithm. If a small error is ok, then do:

在某种程度上更难,因为你不知道它只是数字表示是错误的还是它只是一个接近1的实数值。这实际上取决于你的算法的要求。如果一个小错误没问题,那么:

if(a < 1-threshold)

If it's not ok, then you have to use another variable type that is not suffering from the problem.

如果它不正常,那么你必须使用另一个没有遇到问题的变量类型。

#10


-1  

if you have to use floats in checks, round them off and store it in for example a integer. 1f could be 1.0000000000000000000000000000000001 or 0.999999999999999999999999999999999999

如果你必须在支票中使用浮点数,将它们四舍五入并存储在例如整数中。 1f可以是1.0000000000000000000000000000000001或0.99999999999999999999999999999999999999

#1


14  

Since h is a double, it may have been close enough to 1 to print as 1, but it is actually a bit less than 1 so the comparison succeeds. Floating-point numbers do that a lot.

由于h是一个double,它可能已经足够接近1打印为1,但它实际上小于1,因此比较成功。浮点数可以做很多事情。

#2


5  

Check the actual value of h by printing it out with maximum precision. You'll probably find that it is actually slightly less than 1.0.

通过最大精度打印来检查h的实际值。您可能会发现它实际上略小于1.0。

I ran the following code as a test

我运行以下代码作为测试

#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;
    }
}

and the output was "first branch" (using g++ 4.3.2 on Ubuntu 8.10), but Indeera mentioned in a comment that the same code running on Windows XP compiled with VS2005 gives the output "second branch" (thanks, Indeera).

输出是“第一个分支”(在Ubuntu 8.10上使用g ++ 4.3.2),但Indeera在评论中提到,在使用VS2005编译的Windows XP上运行的相同代码给出输出“第二个分支”(谢谢,Indeera)。

You might change your code to compare the differences between h and 0.0 and h and 1.0 to some small delta value.

您可以更改代码以将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

Note that "(h - 0.0)" can be replaced with "h" in this special case. I'm leaving it the way it is for illustrative value.

请注意,在这种特殊情况下,“(h - 0.0)”可以替换为“h”。我将它留给说明价值的方式。

Also note that if you were only making one comparison you'd need to compare the delta to the absolute value of the difference between h and some constant. Since you're checking a range here, the two comparisons ANDed together make a special case where you can bypass the use of abs. If h is a negative value or some positive value greater than 1.0 it will be out of range and fail one of the two tests above.

另请注意,如果您只进行一次比较,则需要将delta与h与某些常量之差的绝对值进行比较。由于你在这里检查一个范围,两个比较ANDed一起构成一个特殊情况,你可以绕过abs的使用。如果h是负值或某个大于1.0的正值,则它将超出范围并且无法通过上述两个测试之一。

#3


4  

Short story: Your tests are incorrect because floating point numbers do not behave like you probably expect them to. Particularly things like "denom == 0" are problematic.

简短的故事:您的测试不正确,因为浮点数的行为与您可能期望的不同。特别是像“denom == 0”这样的东西是有问题的。

Sun has been nice enough to provide this paper online:

Sun已经足够好,可以在线提供这篇论文:

What Every Computer Scientist Should Know About Floating Point Arithmetic

每个计算机科学家应该知道的浮点运算

which is exactly as advertised. Depending on your background it will be an easy read or a big of work, but it really is worth the time for any programmer using floats.

这与宣传的一模一样。根据您的背景,它将是一个简单的阅读或大量的工作,但它确实值得花时间任何程序员使用浮动。

ADDED COMMENT: I'm not suggesting that every programmer will easily understand everything in that paper. Reading over it though will at the very least give a better idea of what floats actually are, what the issues are, and some hints on how to handle things properly.

添加评论:我并不是说每个程序员都会轻易理解该论文中的所有内容。阅读它虽然至少可以更好地了解浮动实际是什么,问题是什么,以及如何正确处理事情的一些提示。

If you want to do a lot of numerical work properly, you're going to have to read up on the various techniques, but that would be a textbook (or several) worth of material. The comments here have already pointed out some of the basics, and linked to more

如果你想要做很多正确的数值工作,你将不得不阅读各种技术,但这将是一本教科书(或几个)的材料。这里的评论已经指出了一些基础知识,并与更多内容相关联

#4


3  

Always allow for rounding errors when comparing floating point values. Rather than testing for equality, something like this is usually what you want:

在比较浮点值时始终允许舍入错误。而不是测试平等,这样的事情通常是你想要的:

if (abs(x - y) < epsilon) ...

where epsilon is some suitably small value, like 0.00001.

其中epsilon是一些适当的小值,如0.00001。

There are several reasons why floating point math isn't accurate. First, not all values can be represented exactly (0.1 for example, can not be represented in binary floating point numbers, in the same way that 1/3 can't be represented in decimals)

浮点数学不准确的原因有几个。首先,并非所有值都可以准确表示(例如,0.1不能用二进制浮点数表示,与1/3不能用小数表示的方式相同)

Another is that floating point only uses a fixed number of significant digits (which "float" relative to the decimal point, hence the name). So on large numbers, small fractions effectively get truncated away. Never assume that floating point computations return an accurate result.

另一个是浮点只使用固定数量的有效数字(相对于小数点“浮动”,因此名称)。因此,在大数字上,小分数有效地被截断。永远不要假设浮点计算返回准确的结果。

#5


1  

It might be a precision issue. H might not be exactly 1, but something very near to it. Could you post some more information on what you're trying to do, for instance, where does the value of H come from, and where does it go?

这可能是一个精确的问题。 H可能不完全是1,但非常接近它。你能否发布一些关于你想要做什么的更多信息,例如,H的价值来自何处,它在哪里?

#6


0  

It could have to do with the fact that doubles in C/C++ are 64bit, but computation could be done at higher precision (the floating point registers of your cpu are wider (96 bit), so that not even a statement like cos(x)==cos(x) could be true.

它可能与C / C ++中的双精度为64位这一事实有关,但计算可以以更高的精度完成(cpu的浮点寄存器更宽(96位),因此甚至不像cos(x)这样的语句)== cos(x)可能是真的。

Reference: http://www.parashift.com/c++-faq-lite/newbie.html#faq-29.18

#7


0  

Okay, you've posted the code. You are calculating h by a series of arithmetic operations from what looks like fairly arbitrary numbers. This means you're going to get a very close approximation to the ideal value of h, but not quite the right one.

好的,你已经发布了代码。你是通过一系列算术运算来计算h,看起来像是相当任意的数字。这意味着你将获得非常接近h的理想值,但不是非常接近正确值。

This means that you need to do approximate comparisons. Testing (h == 1.0) will succeed only by accident; try (fabs(h - 1.0) < 1e-10) or something like that (using a const double instead of a magic number for tolerance). Make the appropriate changes for other comparisons.

这意味着您需要进行近似比较。测试(h == 1.0)只会偶然成功;尝试(fabs(h - 1.0)<1e-10)或类似的东西(使用const double代替公差的幻数)。对其他比较进行适当的更改。

#8


0  

You may be very interested in the Numerical Robustness for Geometric Calculations (aka "EPSILON is NOT 0.00001!") talk given at GDC 2005.

您可能对GDC 2005中给出的几何计算的数值稳健性(又名“EPSILON is 0.00001!”)非常感兴趣。

#9


0  

The reason is that floating point numbers are not a real representation of the number that you save in the variable. (Opposed to BCD [binary coded decimals])

原因是浮点数不是您在变量中保存的数字的真实表示。 (反对BCD [二进制编码小数])

You can see the definition here: Floating Point Wikipedia

你可以在这里看到定义:浮点*

So the problem is that certain numbers are not expressable with a given set of bits. (You could if you could add bits infinitely) The trick is that in most cases the difference between the saved number and the intended number are pretty small. In practice, you have some corner cases where this may lead to problems. This is for example the reason why you should not build a financial software and use floating point numbers for money calculations. You can easily have differences that are noticeable, which your tax office won't like.

所以问题是某些数字不能用给定的一组比特来表达。 (如果你可以无限地添加位,你可以这样做)诀窍是,在大多数情况下,保存的数字和预期数量之间的差异非常小。在实践中,你有一些可能导致问题的极端情况。这就是为什么你不应该建立财务软件并使用浮点数进行货币计算的原因。您可以轻松获得明显的差异,税务机关不会喜欢这些差异。

So to compare floating point numbers you should always apply some kind of threshhold that is ok for your application. Something like:

因此,为了比较浮点数,您应该始终应用某种适用于您的应用程序的阈值。就像是:

if(a==b) 

becomes

if(abs(a-b)<threshold)

Edit: As David Mentioned in his comment, you will still get into trouble with things like pi, 1./3., ... But you can at least store numbers without loss in precision that you put into the system. As computers have limited memory you can always construct corner cases where you can't rely on exact representations...

编辑:正如David在他的评论中提到的那样,你仍然会遇到pi,1/3等问题。但是你至少可以存储数字,而不会丢失你输入系统的精确度。由于计算机的内存有限,您可以随时构建不能依赖精确表示的极端情况......

Just saw your edit of the text, so here is the next edit:

刚刚看到你对文本的编辑,所以这是下一个编辑:

if(a<1)

Is somehow harder, because you don't know whether it's just the number representation that is wrong or if it's just a real value that is close to 1. It really depends on the requirements for your algorithm. If a small error is ok, then do:

在某种程度上更难,因为你不知道它只是数字表示是错误的还是它只是一个接近1的实数值。这实际上取决于你的算法的要求。如果一个小错误没问题,那么:

if(a < 1-threshold)

If it's not ok, then you have to use another variable type that is not suffering from the problem.

如果它不正常,那么你必须使用另一个没有遇到问题的变量类型。

#10


-1  

if you have to use floats in checks, round them off and store it in for example a integer. 1f could be 1.0000000000000000000000000000000001 or 0.999999999999999999999999999999999999

如果你必须在支票中使用浮点数,将它们四舍五入并存储在例如整数中。 1f可以是1.0000000000000000000000000000000001或0.99999999999999999999999999999999999999