C++ 浮点/双重相等,精确为零
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/9542391/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
float/double equality with exact zero
提问by Bingo
I have an algorithm which uses floats
or doubles
to perform some calculations.
我有一个使用floats
或doubles
执行一些计算的算法。
Example:
例子:
double a;
double b;
double c;
...
double result = c / (b - a);
if ((result > 0) && (result < small_number))
{
// result is relevant...
} else {
// result not required...
}
Now, I am worried about (b - a)
might be zero. If it is close to zero but not zero, it does not matter because the result
will be out of range to be useful, and I already detect that (as (b - a)
approaches zero, result
will approach +/- inf, which is not in the range 0
-small_number
...)
现在,我担心的(b - a)
可能是零。如果它接近于零但不为零,则无关紧要,因为result
将超出有用范围,并且我已经检测到(随着(b - a)
接近零,result
将接近 +/- inf,这不在范围内0
- small_number
。 ..)
But if the result of (b - a)
is exactly zero, I expect that something platform dependant will happen due to divide by zero. I could change the if
statement to:
但是,如果 的结果(b - a)
恰好为零,我预计会因除以零而发生依赖于平台的事情。我可以将if
语句更改为:
if ((!((b-a) == 0.0)) && ((result = c/(b-a)) > 0) && (result < small_number)) {
but I don't know if (b-a) == 0.0
will always detect equality with zero. I have seen there are multiple representations for exact zero in floating point? How can you test for them all without doing some epsilon check, which I don't need (a small epsilon will be ignored in my algorithm)?
但我不知道是否(b-a) == 0.0
会始终检测到与零相等。我已经看到浮点数中有多种表示精确零?你怎么能在不做一些 epsilon 检查的情况下测试它们,我不需要(我的算法中会忽略一个小的 epsilon)?
What is the platform independant way to check?
什么是平台无关的检查方式?
EDIT:
编辑:
Not sure if it was clear enough to people. Basically I want to know how to find if an expression like:
不知道这对人们来说是否足够清楚。基本上我想知道如何查找表达式是否如下:
double result = numerator / denominator;
will result in a floating point exception, a cpu exception, a signal from the operating system or something else.... without actually performing the operating and seeing if it will "throw"... because detecting a "throw" of this nature seems to be complicated and platform specific.
将导致浮点异常、cpu 异常、来自操作系统的信号或其他东西......没有实际执行操作并查看它是否会“抛出”......因为检测到这种性质的“抛出”似乎很复杂且特定于平台。
Is ( (denominator==0.0) || (denominator==-0.0) ) ? "Will 'throw'" : "Won't 'throw'";
enough?
是否( (denominator==0.0) || (denominator==-0.0) ) ? "Will 'throw'" : "Won't 'throw'";
足够?
回答by wallyk
It depends on how b
and a
got their values. Zero has an exact representation in floating point format, but the bigger problem would be almost-but-not-quite zero values. It would always be safe to check:
这取决于如何b
和a
得到了他们的价值。零具有浮点格式的精确表示,但更大的问题是几乎但不完全为零的值。检查以下内容总是安全的:
if (abs(b-a) > 0.00000001 && ...
Where 0.00000001 is whatever value makes sense.
其中 0.00000001 是任何有意义的值。
回答by TonyK
Here's how you do it: instead of checking for (result < small_number)
, you check for
这样做的方法如下:不是检查(result < small_number)
,而是检查
(abs(c) < abs(b - a) * small_number)
Then all your troubles disappear! The computation of c/(b-a)
will never overflow if this test is passed.
然后你所有的烦恼都消失了!c/(b-a)
如果此测试通过,的计算将永远不会溢出。
回答by ony
I guess you can use fpclassify(-0.0) == FP_ZERO
. But this is only useful if you want to check if someone did put some kind of zero into float-type variable. As many already said if you want to check result of calculation you may get values very close to zero due to nature of representation.
我想你可以使用fpclassify(-0.0) == FP_ZERO
. 但这仅在您想检查是否有人确实将某种零放入浮点型变量时才有用。正如许多人已经说过的那样,如果您想检查计算结果,由于表示的性质,您可能会得到非常接近于零的值。
回答by Patric
In brief, we can know a floating number is ZERO exactly if we know it represent format.
简而言之,如果我们知道浮点数代表格式,我们就可以确切地知道它是零。
In practice, we compare x
with a small number. And if x
is less than this number, we think x
is as same as ZERO functionally (but most of time our small number is still large than zero). This method is very easy, efficient and can cross platform.
在实践中,我们x
与一小部分进行比较。如果x
小于这个数字,我们认为x
在功能上与零相同(但大多数时候我们的小数字仍然大于零)。这种方法非常简单、高效并且可以跨平台。
Actually, the float
and double
have been presented by special format, and the widely used one is IEEE 754in current hardware which divided the number into sign, exponent and mantissa (significand) bits.
实际上,float
和double
已经以特殊格式表示, 目前硬件中广泛使用的一种是IEEE 754,它将数字分为符号、指数和尾数(有效数)位。
So, if we want to check if a float number is ZEROexactly, we can check if both exponent and mantissa is ZERO, see here.
所以,如果我们要检查,如果浮点数是ZERO正好,我们可以检查,如果这两个指数和尾数是零,看这里。
In IEEE 754 binary floating point numbers, zero values are represented by the biased exponent and significand both being zero. Negative zero has the sign bit set to one.
在 IEEE 754 二进制浮点数中,零值由偏置指数和有效数都为零表示。负零的符号位设置为 1。
Take float
for example, we can write a simple code to extract exponent and mantissa bit and then check it.
就拿float
例如,我们可以写一个简单的代码来提取指数和尾数位,然后检查。
#include <stdio.h>
typedef union {
float f;
struct {
unsigned int mantissa : 23;
unsigned int exponent : 8;
unsigned int sign : 1;
} parts;
} float_cast;
int isZero(float num) {
int flag = 0;
float_cast data;
data.f = num;
// Check both exponent and mantissa parts
if(data.parts.exponent == 0u && data.parts.mantissa == 0u) {
flag = 1;
} else {
flag = 0;
}
return(flag);
}
int main() {
float num1 = 0.f, num2 = -0.f, num3 = 1.2f;
printf("\n is zero of %f -> %d", num1, isZero(num1));
printf("\n is zero of %f -> %d", num2, isZero(num2));
printf("\n is zero of %f -> %d", num3, isZero(num3));
return(0);
}
Test results:
检测结果:
# is zero of 0.000000 -> 1
# is zero of -0.000000 -> 1
# is zero of 1.200000 -> 0
# 是 0.000000 的零 -> 1
# 是 -0.000000 的零 -> 1
# 是 1.200000 的零 -> 0
More examples:
更多例子:
Let's check when the float
becomes real ZERO with code.
让我们float
用代码检查什么时候变成真正的零。
void test() {
int i =0;
float e = 1.f, small = 1.f;
for(i = 0; i < 40; i++) {
e *= 10.f;
small = 1.f/e;
printf("\nis %e zero? : %d", small, isZero(small));
}
return;
}
is 1.0000e-01 zero? : NO
is 1.0000e-02 zero? : NO
is 1.0000e-03 zero? : NO
is 1.0000e-04 zero? : NO
is 1.0000e-05 zero? : NO
is 1.0000e-06 zero? : NO
is 1.0000e-07 zero? : NO
is 1.0000e-08 zero? : NO
is 1.0000e-09 zero? : NO
is 1.0000e-10 zero? : NO
is 1.0000e-11 zero? : NO
is 1.0000e-12 zero? : NO
is 1.0000e-13 zero? : NO
is 1.0000e-14 zero? : NO
is 1.0000e-15 zero? : NO
is 1.0000e-16 zero? : NO
is 1.0000e-17 zero? : NO
is 1.0000e-18 zero? : NO
is 1.0000e-19 zero? : NO
is 1.0000e-20 zero? : NO
is 1.0000e-21 zero? : NO
is 1.0000e-22 zero? : NO
is 1.0000e-23 zero? : NO
is 1.0000e-24 zero? : NO
is 1.0000e-25 zero? : NO
is 1.0000e-26 zero? : NO
is 1.0000e-27 zero? : NO
is 1.0000e-28 zero? : NO
is 1.0000e-29 zero? : NO
is 1.0000e-30 zero? : NO
is 1.0000e-31 zero? : NO
is 1.0000e-32 zero? : NO
is 1.0000e-33 zero? : NO
is 1.0000e-34 zero? : NO
is 1.0000e-35 zero? : NO
is 1.0000e-36 zero? : NO
is 1.0000e-37 zero? : NO
is 1.0000e-38 zero? : NO
is 0.0000e+00 zero? : YES <-- 1e-39
is 0.0000e+00 zero? : YES <-- 1e-40
回答by Aaron Johnson
UPDATE (2016-01-04)
更新 (2016-01-04)
I've received some downvotes on this answer, and I wondered if I should just delete it. It seems the consensus (https://meta.stackexchange.com/questions/146403/should-i-delete-my-answers) is that deleting answers should only be done in extreme cases.
我收到了对这个答案的一些反对票,我想知道我是否应该删除它。似乎共识(https://meta.stackexchange.com/questions/146403/should-i-delete-my-answers)是删除答案只应在极端情况下进行。
So, my answer is wrong. But I guess I'm leaving it up because it provides for an interesting "think out of the box" kind of thought experiment.
所以,我的答案是错误的。但我想我放弃它是因为它提供了一种有趣的“开箱即用”的思想实验。
===============
================
Bingo,
答对了,
You say you want to know if b-a == 0.
你说你想知道 ba == 0 。
Another way of looking at this is to determine whether a == b. If a equals b, then b-a will be equal 0.
另一种看待这个的方法是确定是否 a == b。如果 a 等于 b,则 ba 将等于 0。
Another interesting idea I found:
我发现的另一个有趣的想法:
http://www.cygnus-software.com/papers/comparingfloats/Comparing%20floating%20point%20numbers.htm
http://www.cygnus-software.com/papers/comparingfloats/Comparing%20floating%20point%20numbers.htm
Essentially, you take the floating point variables you have and tell the compiler to reinterpret them (bit for bit) as signed integers, as in the following:
本质上,您使用您拥有的浮点变量并告诉编译器将它们(逐位)重新解释为有符号整数,如下所示:
if (*(int*)&b == *(int*)&a)
Then you are comparing integers, and not floating points. Maybe that will help? Maybe not. Good luck!
然后你比较的是整数,而不是浮点数。也许这会有所帮助?也许不吧。祝你好运!
回答by David L.
I believe that (b-a)==0
will be true exactly in those cases when the c/(b-a)
would fail because of (b-a)
being zero. The float maths is tricky but questioning this is exaggerating in my opinion. Also I believe that the (b-a)==0
is going to be equivalent to b!=a
.
我相信(b-a)==0
在那些c/(b-a)
因为(b-a)
为零而失败的情况下,这将是正确的。浮动数学很棘手,但在我看来质疑这一点是夸大其词。我也相信(b-a)==0
将等同于b!=a
.
Distinguishing positive and negative 0 is also not necessary. See e.g. here Does float have a negative zero? (-0f)
也没有必要区分正负 0。参见例如这里float 是否有负零?(-0f)
回答by mcjoan
For epsilon, in there is a standard template definition std::numeric_limits::epsilon(). I guess checking the difference to be bigger than std::numeric_limits::epsilon() should be safe enough to protect against division by zero. No platform dependency here I guess.
对于 epsilon,有一个标准模板定义 std::numeric_limits::epsilon()。我想检查差异是否大于 std::numeric_limits::epsilon() 应该足够安全以防止被零除。我猜这里没有平台依赖性。
回答by Mark
You could try
你可以试试
if ((b-a)!=(a-b) && ((result = c/(b-a)) > 0) && (result < small_number))) {
...