Mic*_*ann 52 c++ floating-point
C++ FAQ lite "[29.17]为什么我的浮点比较不起作用?" 建议这个相等测试:
#include <cmath> /* for std::abs(double) */
inline bool isEqual(double x, double y)
{
const double epsilon = /* some small number such as 1e-5 */;
return std::abs(x - y) <= epsilon * std::abs(x);
// see Knuth section 4.2.2 pages 217-218
}
Run Code Online (Sandbox Code Playgroud)
+0
和-0
?|x| < epsilon
?更新
正如Daniel Daranas所指出的那样,最好调用函数isNearlyEqual
(我关心的是这种情况).
有人指出了这个链接,我想更加突出地分享.
Ben*_*igt 42
你的观察是正确的.
如果x == 0.0
,则abs(x) * epsilon
为零并且您正在测试是否abs(y) <= 0.0
.
如果y == 0.0
那时你正在测试abs(x) <= abs(x) * epsilon
哪种方式epsilon >= 1
(它不是)或x == 0.0
.
所以,无论是is_equal(val, 0.0)
或者is_equal(0.0, val)
没有意义,你可以这样说val == 0.0
.如果你想只接受准确 +0.0
和-0.0
.
常见问题解答在这种情况下的推荐是有限的效用. 没有"一刀切"的浮点比较. 您必须考虑变量的语义,可接受的值范围以及计算引入的错误大小.甚至常见问题解答提到了一个警告,说这个功能通常不是一个问题"当x和y的幅度明显大于epsilon,但你的里程可能会有所不同".
Dan*_*nas 17
没有.
平等就是平等.
你编写的函数不会测试两个双精度的等价,正如它的名字所承诺的那样.它只会测试两个双打是否足够"接近".
如果你真的想测试两个双打的相等性,那就用这个:
inline bool isEqual(double x, double y)
{
return x == y;
}
Run Code Online (Sandbox Code Playgroud)
编码标准通常建议不要将两个双精度数进行比较以确保相等.但那是一个不同的主题.如果你真的想要比较两个完全相等的双打,x == y
那么你需要的是代码.
无论他们告诉你什么,10.00000000000000001都不等于10.0.
使用精确相等的一个示例是当double的特定值用作某个特殊状态的同义词时,例如"pending calulation"或"no data available".仅当挂起计算之后的实际数值仅是double的可能值的子集时,才可以执行此操作.最典型的特殊情况是该值为非负值,并使用-1.0作为"待定计算"或"无可用数据"的(精确)表示.你可以用常数表示:
const double NO_DATA = -1.0;
double myData = getSomeDataWhichIsAlwaysNonNegative(someParameters);
if (myData != NO_DATA)
{
...
}
Run Code Online (Sandbox Code Playgroud)
您可以使用std::nextafter
固定factor
的epsilon
值,如下所示:
bool isNearlyEqual(double a, double b)
{
int factor = /* a fixed 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;
}
Run Code Online (Sandbox Code Playgroud)
2 + 2 = 5(*)
(对于某些浮点精度值 2)
当我们将“浮点”视为提高精度的一种方法时,这个问题经常出现。然后我们就遇到了“浮动”部分,这意味着无法保证可以表示哪些数字。
因此,虽然我们可能很容易能够表示“1.0,-1.0,0.1,-0.1”,但当我们得到更大的数字时,我们开始看到近似值 - 或者我们应该,除非我们经常通过截断显示的数字来隐藏它们。
因此,我们可能认为计算机正在存储“0.003”,但实际上它可能存储的是“0.0033333333334”。
如果执行“0.0003 - 0.0002”会发生什么?我们期望 0.0001,但存储的实际值可能更像“0.00033” - “0.00029”,它产生“0.000004”,或者最接近的可表示值,它可能是 0,也可能是“0.000006”。
对于当前的浮点数学运算,不能保证 (a / b) * b == a。
#include <stdio.h>
// defeat inline optimizations of 'a / b * b' to 'a'
extern double bodge(int base, int divisor) {
return static_cast<double>(base) / static_cast<double>(divisor);
}
int main() {
int errors = 0;
for (int b = 1; b < 100; ++b) {
for (int d = 1; d < 100; ++d) {
// b / d * d ... should == b
double res = bodge(b, d) * static_cast<double>(d);
// but it doesn't always
if (res != static_cast<double>(b))
++errors;
}
}
printf("errors: %d\n", errors);
}
Run Code Online (Sandbox Code Playgroud)
ideone 仅使用 1 <= b <= 100 和 1 <= d <= 100 的 10,000 种组合报告 599 个实例,其中 (b * d) / d != b 。
FAQ 中描述的解决方案本质上是应用粒度约束 - 进行测试if (a == b +/- epsilon)
。
另一种方法是通过使用定点精度或使用所需的粒度作为存储的基本单位来完全避免该问题。例如,如果您希望以纳秒精度存储时间,请使用纳秒作为存储单位。
C++11 引入了std::ratio作为不同时间单位之间定点转换的基础。
归档时间: |
|
查看次数: |
82249 次 |
最近记录: |