C++ 使用 epsilon 将双精度与零进行比较
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/13698927/
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
Compare double to zero using epsilon
提问by Sebastian Krysmanski
Today, I was looking through some C++ code (written by somebody else) and found this section:
今天,我正在浏览一些 C++ 代码(由其他人编写)并找到了这一部分:
double someValue = ...
if (someValue < std::numeric_limits<double>::epsilon() &&
someValue > -std::numeric_limits<double>::epsilon()) {
someValue = 0.0;
}
I'm trying to figure out whether this even makes sense.
我试图弄清楚这是否有意义。
The documentation for epsilon()
says:
的文档epsilon()
说:
The function returns the difference between 1 and the smallest value greater than 1 that is representable [by a double].
该函数返回 1 与可表示的大于 1 的最小值之间的差值 [由双精度]。
Does this apply to 0 as well, i.e. epsilon()
is the smallest value greater than 0? Or are there numbers between 0
and 0 + epsilon
that can be represented by a double
?
这是否也适用于 0,即epsilon()
最小值是否大于 0?或者0
和之间是否有数字0 + epsilon
可以用 a 表示double
?
If not, then isn't the comparison equivalent to someValue == 0.0
?
如果不是,那么比较不等于someValue == 0.0
?
采纳答案by ybungalobill
Assuming 64-bit IEEE double, there is a 52-bit mantissa and 11-bit exponent. Let's break it to bits:
假设 64 位 IEEE 双精度数,则有 52 位尾数和 11 位指数。让我们把它分成几部分:
1.0000 00000000 00000000 00000000 00000000 00000000 00000000 × 2^0 = 1
The smallest representable number greater than 1:
大于 1 的最小可表示数:
1.0000 00000000 00000000 00000000 00000000 00000000 00000001 × 2^0 = 1 + 2^-52
Therefore:
所以:
epsilon = (1 + 2^-52) - 1 = 2^-52
Are there any numbers between 0 and epsilon? Plenty... E.g. the minimal positive representable (normal) number is:
0和epsilon之间有数字吗?很多......例如,最小的正可表示(正常)数是:
1.0000 00000000 00000000 00000000 00000000 00000000 00000000 × 2^-1022 = 2^-1022
In fact there are (1022 - 52 + 1)×2^52 = 4372995238176751616
numbers between 0 and epsilon, which is 47% of all the positive representable numbers...
事实上,(1022 - 52 + 1)×2^52 = 4372995238176751616
在 0 和 epsilon 之间有一些数字,这是所有可表示的正数的 47%...
回答by Steve Jessop
The test certainly is not the same as someValue == 0
. The whole idea of floating-point numbers is that they store an exponent and a significand. They therefore represent a value with a certain number of binary significant figures of precision (53 in the case of an IEEE double). The representable values are much more densely packed near 0 than they are near 1.
测试肯定不一样someValue == 0
。浮点数的整个想法是它们存储一个指数和一个有效数。因此,它们表示具有一定数量的二进制有效数字精度的值(在 IEEE 双精度的情况下为 53)。可表示的值在 0 附近比在 1 附近更密集。
To use a more familiar decimal system, suppose you store a decimal value "to 4 significant figures" with exponent. Then the next representable value greater than 1
is 1.001 * 10^0
, and epsilon
is 1.000 * 10^-3
. But 1.000 * 10^-4
is also representable, assuming that the exponent can store -4. You can take my word for it that an IEEE double canstore exponents less than the exponent of epsilon
.
要使用更熟悉的十进制系统,假设您将十进制值存储为“到 4 个有效数字”和指数。那么下一个可表示的值大于1
is 1.001 * 10^0
,并且epsilon
是1.000 * 10^-3
。但1.000 * 10^-4
也是可表示的,假设指数可以存储 -4。你可以相信我的话,IEEE double可以存储小于 的指数的指数epsilon
。
You can't tell from this code alone whether it makes sense or not to use epsilon
specifically as the bound, you need to look at the context. It may be that epsilon
is a reasonable estimate of the error in the calculation that produced someValue
, and it may be that it isn't.
您无法仅从这段代码中判断epsilon
专门用作边界是否有意义,您需要查看上下文。这可能是对epsilon
产生 的计算中的错误的合理估计someValue
,也可能不是。
回答by Skizz
There are numbers that exist between 0 and epsilon because epsilon is the difference between 1 and the next highest number that can be represented above 1 and not the difference between 0 and the next highest number that can be represented above 0 (if it were, that code would do very little):-
有些数字存在于 0 和 epsilon 之间,因为 epsilon 是 1 和可以在 1 以上表示的下一个最高数字之间的差,而不是 0 和可以在 0 以上表示的下一个最高数字之间的差(如果是,那代码会做的很少):-
#include <limits>
int main ()
{
struct Doubles
{
double one;
double epsilon;
double half_epsilon;
} values;
values.one = 1.0;
values.epsilon = std::numeric_limits<double>::epsilon();
values.half_epsilon = values.epsilon / 2.0;
}
Using a debugger, stop the program at the end of main and look at the results and you'll see that epsilon / 2 is distinct from epsilon, zero and one.
使用调试器,在 main 的末尾停止程序并查看结果,您将看到 epsilon / 2 与 epsilon、零和一不同。
So this function takes values between +/- epsilon and makes them zero.
因此,此函数采用 +/- epsilon 之间的值并使它们为零。
回答by pbhd
An aproximation of epsilon (smallest possible difference) around a number (1.0, 0.0, ...) can be printed with the following program. It prints the following output: epsilon for 0.0 is 4.940656e-324
epsilon for 1.0 is 2.220446e-16
A little thinking makes it clear, that the epsilon gets smaller the more smaller the number is we use for looking at its epsilon-value, because the exponent can adjust to the size of that number.
可以使用以下程序打印数字 (1.0, 0.0, ...) 附近的 epsilon(最小可能差异)的近似值。它会打印以下输出:epsilon for 0.0 is 4.940656e-324
epsilon for 1.0 is 2.220446e-16
稍微思考一下就清楚了,我们用于查看其 epsilon 值的数字越小,epsilon 越小,因为指数可以调整为该数字的大小。
#include <stdio.h>
#include <assert.h>
double getEps (double m) {
double approx=1.0;
double lastApprox=0.0;
while (m+approx!=m) {
lastApprox=approx;
approx/=2.0;
}
assert (lastApprox!=0);
return lastApprox;
}
int main () {
printf ("epsilon for 0.0 is %e\n", getEps (0.0));
printf ("epsilon for 1.0 is %e\n", getEps (1.0));
return 0;
}
回答by Yakk - Adam Nevraumont
Suppose we are working with toy floating point numbers that fit in a 16 bit register. There is a sign bit, a 5 bit exponent, and a 10 bit mantissa.
假设我们正在使用适合 16 位寄存器的玩具浮点数。有一个符号位、一个 5 位指数和一个 10 位尾数。
The value of this floating point number is the mantissa, interpreted as a binary decimal value, times two to the power of the exponent.
此浮点数的值是尾数,解释为二进制十进制值,乘以 2 的指数次方。
Around 1 the exponent equals zero. So the smallest digit of the mantissa is one part in 1024.
大约 1 时,指数为零。所以尾数的最小位数是 1024 的一部分。
Near 1/2 the exponent is minus one, so the smallest part of the mantissa is half as large. With a five bit exponent it can reach negative 16, at which point the smallest part of the mantissa is worth one part in 32m. And at negative 16 exponent, the value is around one part in 32k, much closer to zero than the epsilon around one we calculated above!
接近 1/2 的指数是负一,所以尾数的最小部分是它的一半。使用五位指数,它可以达到负 16,此时尾数的最小部分相当于 32m 的一部分。在负 16 指数处,该值大约是 32k 的一部分,比我们上面计算的大约 1 的 epsilon 更接近于零!
Now this is a toy floating point model that does not reflect all the quirks of a real floating point system , but the ability to reflect values smaller than epsilon is reasonably similar with real floating point values.
现在这是一个玩具浮点模型,它不能反映真实浮点系统的所有怪癖,但反映小于 epsilon 的值的能力与真实浮点值相当相似。
回答by Daniel Laügt
The difference between X
and the next value of X
varies according to X
.epsilon()
is only the difference between 1
and the next value of 1
.
The difference between 0
and the next value of 0
is not epsilon()
.
X
和 的下一个值之间的差值根据X
不同而不同X
。epsilon()
只是1
和 的下一个值之间的差值1
。和 的下一个值
之间的差异不是。0
0
epsilon()
Instead you can use std::nextafter
to compare a double value with 0
as the following:
相反,您可以使用以下std::nextafter
方式比较双精度值0
:
bool same(double a, double b)
{
return std::nextafter(a, std::numeric_limits<double>::lowest()) <= b
&& std::nextafter(a, std::numeric_limits<double>::max()) >= b;
}
double someValue = ...
if (same (someValue, 0.0)) {
someValue = 0.0;
}
回答by supercat
With IEEE floating-point, between the smallest non-zero positive value and the smallest non-zero negative value, there exist two values: positive zero and negative zero. Testing whether a value is between the smallest non-zero values is equivalent to testing for equality with zero; the assignment, however, may have an effect, since it would change a negative zero to a positive zero.
对于 IEEE 浮点,在最小的非零正值和最小的非零负值之间,存在两个值:正零和负零。测试一个值是否在最小的非零值之间等同于测试是否与零相等;然而,赋值可能会产生影响,因为它会将负零变为正零。
It would be conceivable that a floating-point format might have three values between the smallest finite positive and negative values: positive infinitesimal, unsigned zero, and negative infinitesimal. I am not familiar with any floating-point formats that in fact work that way, but such a behavior would be perfectly reasonable and arguably better than that of IEEE (perhaps not enough better to be worth adding extra hardware to support it, but mathematically 1/(1/INF), 1/(-1/INF), and 1/(1-1) should represent three distinct cases illustrating three different zeroes). I don't know whether any C standard would mandate that signed infinitesimals, if they exist, would have to compare equal to zero. If they do not, code like the above could usefully ensure that e.g. dividing a number repeatedly by two would eventually yield zero rather than being stuck on "infinitesimal".
可以想象,浮点格式可能具有最小有限正值和负值之间的三个值:正无穷小、无符号零和负无穷小。我不熟悉任何实际上以这种方式工作的浮点格式,但这种行为将是完全合理的,并且可以说比 IEEE 更好(也许还不够好,值得添加额外的硬件来支持它,但在数学上 1 /(1/INF)、1/(-1/INF) 和 1/(1-1) 应该代表三个不同的情况,说明三个不同的零)。我不知道是否有任何 C 标准会要求签署无穷小,如果它们存在,则必须比较为零。如果他们不这样做,像上面这样的代码可以有效地确保例如
回答by Arsenii Fomin
You can't apply this to 0, because of mantissa and exponent parts. Due to exponent you can store very little numbers, which are smaller than epsilon, but when you try to do something like (1.0 - "very small number") you'll get 1.0. Epsilon is an indicator not of value, but of value precision, which is in mantissa. It shows how many correct consequent decimal digits of number we can store.
由于尾数和指数部分,您不能将此应用于 0。由于指数,您可以存储非常少的数字,这些数字比 epsilon 小,但是当您尝试执行类似 (1.0 - “非常小的数字”) 之类的操作时,您将获得 1.0。Epsilon 不是价值的指标,而是价值精度的指标,以尾数表示。它显示了我们可以存储多少个正确的后续十进制数字。
回答by Luca Davanzo
I think that depend on the precisionof your computer. Take a look on this table: you can see that if your epsilon is represented by double, but your precision is higher, the comparison is not equivalent to
我认为这取决于您的计算机的精度。看看这张表:你可以看到如果你的epsilon用double表示,但你的精度更高,比较不等于
someValue == 0.0
Good question anyway!
无论如何,好问题!
回答by cababunga
So let's say system cannot distinguish 1.000000000000000000000 and 1.000000000000000000001. that is 1.0 and 1.0 + 1e-20. Do you think there still are some values that can be represented between -1e-20 and +1e-20?
所以假设系统无法区分 1.000000000000000000000 和 1.000000000000000000001。即 1.0 和 1.0 + 1e-20。您认为在-1e-20 和+1e-20 之间还有一些值可以表示吗?