C++ 如何正确和标准地比较浮点数?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4548004/
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
How to correctly and standardly compare floats?
提问by Dmitriy
Every time I start a new project and when I need to compare some float or double variables I write the code like this one:
每次我开始一个新项目时,当我需要比较一些 float 或 double 变量时,我都会编写这样的代码:
if (fabs(prev.min[i] - cur->min[i]) < 0.000001 &&
fabs(prev.max[i] - cur->max[i]) < 0.000001) {
continue;
}
Then I want to get rid of these magic variables 0.000001(and 0.00000000001 for double) and fabs, so I write an inline function and some defines:
然后我想摆脱这些魔法变量 0.000001(和 0.00000000001 为双)和晶圆厂,所以我写了一个内联函数和一些定义:
#define FLOAT_TOL 0.000001
So I wonder if there is any standard way of doing this? May be some standard header file? It would be also nice to have float and double limits(min and max values)
所以我想知道是否有任何标准的方法来做到这一点?可能是一些标准的头文件?有浮动和双重限制(最小值和最大值)也很好
采纳答案by Dmitriy
Thanks for your answers, they helped me a lot. I've read these materials:firstand second
谢谢你的回答,他们帮了我很多。我已经阅读了这些材料:第一和第二
The answer is to use my own function for relative comparison:
答案是使用我自己的函数进行相对比较:
bool areEqualRel(float a, float b, float epsilon) {
return (fabs(a - b) <= epsilon * std::max(fabs(a), fabs(b)));
}
This is the most suitable solution for my needs. However I've wrote some tests and other comparison methods. I hope this will be useful for somebody. areEqualRel passes these tests, others don't.
这是最适合我需求的解决方案。但是我已经写了一些测试和其他比较方法。我希望这对某人有用。areEqualRel 通过了这些测试,其他的则没有。
#include <iostream>
#include <limits>
#include <algorithm>
using std::cout;
using std::max;
bool areEqualAbs(float a, float b, float epsilon) {
return (fabs(a - b) <= epsilon);
}
bool areEqual(float a, float b, float epsilon) {
return (fabs(a - b) <= epsilon * std::max(1.0f, std::max(a, b)));
}
bool areEqualRel(float a, float b, float epsilon) {
return (fabs(a - b) <= epsilon * std::max(fabs(a), fabs(b)));
}
int main(int argc, char *argv[])
{
cout << "minimum: " << FLT_MIN << "\n";
cout << "maximum: " << FLT_MAX << "\n";
cout << "epsilon: " << FLT_EPSILON << "\n";
float a = 0.0000001f;
float b = 0.0000002f;
if (areEqualRel(a, b, FLT_EPSILON)) {
cout << "are equal a: " << a << " b: " << b << "\n";
}
a = 1000001.f;
b = 1000002.f;
if (areEqualRel(a, b, FLT_EPSILON)) {
cout << "are equal a: " << a << " b: " << b << "\n";
}
}
回答by Michael Borgwardt
From The Floating-Point Guide:
来自浮点指南:
This is a bad way to do it because a fixed epsilon chosen because it “looks small” could actually be way too large when the numbers being compared are very small as well. The comparison would return “true” for numbers that are quite different. And when the numbers are very large, the epsilon could end up being smaller than the smallest rounding error, so that the comparison always returns “false”.
这是一个糟糕的方法,因为当被比较的数字也非常小时,选择一个固定的 epsilon 因为它“看起来很小”实际上可能太大了。对于完全不同的数字,比较将返回“真”。当数字非常大时,epsilon 最终可能小于最小的舍入误差,因此比较总是返回“false”。
The problem with the "magic number" here is not that it's hardcoded but that it's "magic": you didn't really have a reason for choosing 0.000001 over 0.000005 or 0.0000000000001, did you? Note that float
can approximately represent the latter and still smaller values - it's just about 7 decimals of precision afterthe first nonzero digit!
这里“神奇数字”的问题不在于它是硬编码的,而是它的“神奇”:你真的没有理由选择 0.000001 而不是 0.000005 或 0.0000000000001,对吗?请注意,float
可以近似表示后者和更小的值 -在第一个非零数字之后大约只有 7 位小数精度!
If you're going to use a fixed epsilon, you should really choose it according to the requirements of the particular piece of code where you use it. The alternative is to use a relative error margin (see link at the top for details) or, even better, or compare the floats as integers.
如果您打算使用固定的 epsilon,您应该根据使用它的特定代码段的要求来选择它。另一种方法是使用相对误差幅度(有关详细信息,请参阅顶部的链接),或者更好,或者将浮点数作为整数进行比较。
回答by Puppy
The Standard provides an epsilon value. It's in <limits>
and you can access the value by std::numeric_limits<float>::epsilon
and std::numeric_limits<double>::epsilon
. There are other values in there, but I didn't check what exactly is.
标准提供了一个 epsilon 值。它在<limits>
,您可以通过std::numeric_limits<float>::epsilon
和访问该值std::numeric_limits<double>::epsilon
。那里还有其他值,但我没有检查到底是什么。
回答by Daniel Laügt
You can use std::nextafter
for testing two double
with the smallest epsilon on a value (or a factor of the smallest epsilon).
您可以std::nextafter
用于测试两个double
具有最小 epsilon 的值(或最小 epsilon 的因子)。
bool nearly_equal(double a, double b)
{
return std::nextafter(a, std::numeric_limits<double>::lowest()) <= b
&& std::nextafter(a, std::numeric_limits<double>::max()) >= b;
}
bool nearly_equal(double a, double b, int factor /* a factor of epsilon */)
{
double min_a = a - (a - std::nextafter(a, std::numeric_limits<double>::lowest())) * factor;
double max_a = a + (std::nextafter(a, std::numeric_limits<double>::max()) - a) * factor;
return min_a <= b && max_a >= b;
}
回答by ddyer
You should be aware that if you are comparing two floats for equality, you are intrinsically doing the wrong thing. Adding a slop factor to the comparison is not good enough.
您应该意识到,如果您比较两个浮点数是否相等,那么您本质上就是在做错事。在比较中添加斜率因子是不够的。
回答by 0xbadf00d
You should use the standard define in float.h:
您应该使用 float.h 中的标准定义:
#define DBL_EPSILON 2.2204460492503131e-016 /* smallest float value such that 1.0+DBL_EPSILON != 1.0 */
or the numeric_limits class:
或 numeric_limits 类:
// excerpt
template<>
class numeric_limits<float> : public _Num_float_base
{
public:
typedef float T;
// return minimum value
static T (min)() throw();
// return smallest effective increment from 1.0
static T epsilon() throw();
// return largest rounding error
static T round_error() throw();
// return minimum denormalized value
static T denorm_min() throw();
};
[EDIT: Made it just a little bit more readable.]
[编辑:使它更具可读性。]
But in addition, it depends on what you're after.
但此外,这取决于您的追求。
回答by Jelle van Campen
Here is a c++11 implementation of @geotavros 's solution. It makes use of the new std::numeric_limits<T>::epsilon()
function and the fact that std::fabs()
and std::fmax()
now have overloads for float
, double
and long float
.
这是@geotavros 解决方案的 c++11 实现。它利用了新std::numeric_limits<T>::epsilon()
函数以及std::fabs()
andstd::fmax()
现在具有float
,double
和重载的事实long float
。
template<typename T>
static bool AreEqual(T f1, T f2) {
return (std::fabs(f1 - f2) <= std::numeric_limits<T>::epsilon() * std::fmax(std::fabs(f1), std::fabs(f2)));
}
回答by blufiro
This post has a comprehensive explanation of how to compare floating point numbers: http://www.altdevblogaday.com/2012/02/22/comparing-floating-point-numbers-2012-edition/
这篇文章全面解释了如何比较浮点数:http: //www.altdevblogaday.com/2012/02/22/comparing-floating-point-numbers-2012-edition/
Excerpt:
摘抄:
- If you are comparing against zero, then relative epsilons and ULPs based comparisons are usually meaningless. You'll need to use an absolute epsilon, whose value might be some small multiple of FLT_EPSILON and the inputs to your calculation. Maybe.
- If you are comparing against a non-zero number then relative epsilons or ULPs based comparisons are probably what you want. You'll probably want some small multiple of FLT_EPSILON for your relative epsilon, or some small number of ULPs. An absolute epsilon could be used if you knew exactly what number you were comparing against.
- 如果您要与零进行比较,那么基于相对 epsilon 和 ULP 的比较通常毫无意义。您需要使用绝对值 epsilon,其值可能是 FLT_EPSILON 和计算输入的一些小倍数。也许。
- 如果您要与非零数字进行比较,那么基于相对 epsilon 或 ULP 的比较可能就是您想要的。您可能需要一些小的 FLT_EPSILON 倍数作为相对 epsilon,或者一些少量的 ULP。如果您确切知道要比较的数字,则可以使用绝对 epsilon。