关于变量:比较IEEE浮点数和双精度是否相等

关于变量:比较IEEE浮点数和双精度是否相等

Comparing IEEE floats and doubles for equality

比较IEEE浮点数和双精度数是否相等的最佳方法是什么? 我听说过几种方法,但是我想看看社区的想法。


我认为最好的方法是比较ULP。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
bool is_nan(float f)
{
    return (*reinterpret_cast<unsigned __int32*>(&f) & 0x7f800000) == 0x7f800000 && (*reinterpret_cast<unsigned __int32*>(&f) & 0x007fffff) != 0;
}

bool is_finite(float f)
{
    return (*reinterpret_cast<unsigned __int32*>(&f) & 0x7f800000) != 0x7f800000;
}

// if this symbol is defined, NaNs are never equal to anything (as is normal in IEEE floating point)
// if this symbol is not defined, NaNs are hugely different from regular numbers, but might be equal to each other
#define UNEQUAL_NANS 1
// if this symbol is defined, infinites are never equal to finite numbers (as they're unimaginably greater)
// if this symbol is not defined, infinities are 1 ULP away from +/- FLT_MAX
#define INFINITE_INFINITIES 1

// test whether two IEEE floats are within a specified number of representable values of each other
// This depends on the fact that IEEE floats are properly ordered when treated as signed magnitude integers
bool equal_float(float lhs, float rhs, unsigned __int32 max_ulp_difference)
{
#ifdef UNEQUAL_NANS
    if(is_nan(lhs) || is_nan(rhs))
    {
        return false;
    }
#endif
#ifdef INFINITE_INFINITIES
    if((is_finite(lhs) && !is_finite(rhs)) || (!is_finite(lhs) && is_finite(rhs)))
    {
        return false;
    }
#endif
    signed __int32 left(*reinterpret_cast<signed __int32*>(&lhs));
    // transform signed magnitude ints into 2s complement signed ints
    if(left < 0)
    {
        left = 0x80000000 - left;
    }
    signed __int32 right(*reinterpret_cast<signed __int32*>(&rhs));
    // transform signed magnitude ints into 2s complement signed ints
    if(right < 0)
    {
        right = 0x80000000 - right;
    }
    if(static_cast<unsigned __int32>(std::abs(left - right)) <= max_ulp_difference)
    {
        return true;
    }
    return false;
}

类似的技术可以用于双打。诀窍是转换浮点数,以便对它们进行排序(就像整数一样),然后看看它们有何不同。

我不知道为什么这该死的东西搞砸了我的下划线。编辑:哦,也许这只是预览的人工产物。那没关系


我正在使用的当前版本是这个

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
bool is_equals(float A, float B,
               float maxRelativeError, float maxAbsoluteError)
{

  if (fabs(A - B) < maxAbsoluteError)
    return true;

  float relativeError;
  if (fabs(B) > fabs(A))
    relativeError = fabs((A - B) / B);
  else
    relativeError = fabs((A - B) / A);

  if (relativeError <= maxRelativeError)
    return true;

  return false;
}

通过结合相对和绝对错误容忍度,这似乎可以解决大多数问题。 ULP方法更好吗?如果是这样,为什么?


在数值软件中,您通常想测试两个浮点数是否完全相等。对于此类情况,LAPACK充满了示例。当然,最常见的情况是您要测试浮点数是否等于"零","一个","两个","半"。如果有人感兴趣,我可以选择一些算法,并进一步详细介绍。

另外,在BLAS中,您经常需要检查浮点数是正好是零还是1。例如,例程dgemv可以计算以下形式的运算

  • y = beta * y + alpha * A * x
  • y = beta * y + alpha * A ^ T * x
  • y = beta * y + alpha * A ^ H * x

因此,如果beta等于1,则您有一个"加号分配",而对于beta等于0,则有一个"简单分配"。因此,如果对这些(常见)案例进行特殊处理,您当然可以削减计算成本。

当然,您可以以避免完全比较的方式设计BLAS例程(例如,使用某些标志)。但是,LAPACK充满了不可能的示例。

附言:

  • 当然,在很多情况下,您都不希望检查"完全相等"。对于许多人来说,这甚至可能是他们不得不面对的唯一情况。我只想指出的是,还有其他情况。

  • 尽管LAPACK是用Fortran编写的,但是如果您将其他编程语言用于数字软件,则逻辑是相同的。


@DrPizza: I am no performance guru but I would expect fixed point operations to be quicker than floating point operations (in most cases).

而是取决于您对他们的处理方式。与IEEE浮点数具有相同范围的定点类型将慢很多倍(并且大很多倍)。

Things suitable for floats:

3D图形,物理/工程,模拟,气候模拟。...


An int lets me express ~10^9 values
(regardless of the range) which seems
like enough for any situation where I
would care about two of them being
equal. And if that's not enough, use a
64-bit OS and you've got about 10^19
distinct values.

我实际上已经达到了这个极限...我试图在模拟中以ps为单位的时间和以时钟周期为单位的时间,在您轻松达到10 ^ 10周期的情况下。不管我做了什么,我很快都溢出了64位整数的微不足道的范围... 10 ^ 19并不像您想象的那样多,现在给定128位整数运算!

浮点数使我能够解决数学问题,因为低端的值溢出了很多零。因此,您基本上可以在数字上保留小数点后的整数,而不会损失精度(我想与64位int相比,在float的尾数中允许使用更有限的不同数量的值,但是迫切需要这个范围! )。

然后事物转换回整数以进行比较等。

令人烦恼的是,最后我放弃了整个尝试,只是依靠float和<和>来完成工作。并不是很完美,但是可以用于预期的用例。


It rather depends on what you are
doing with them. A fixed-point type
with the same range as an IEEE float
would be many many times slower (and
many times larger).

好的,但是如果我想要一个无限小的位分辨率,那么它又回到了我原来的观点:==和!=在这种问题的上下文中没有任何意义。

一个int让我表达?10 ^ 9个值(无论范围如何),对于任何我关心其中两个相等的情况,这似乎足够了。如果这还不够,请使用64位操作系统,您将获得大约10 ^ 19个不同的值。

我可以在一个int中表示一个范围为0到10 ^ 200的值,例如,位分辨率受到的影响(分辨率将大于1,但是同样,没有应用程序也具有这种范围)作为那种分辨率)。

总而言之,我认为在所有情况下,要么代表一个连续的值,在这种情况下!=和==不相关,或者代表一个固定的值集,可以将其映射为一个int(或另一个固定值)。 -精度类型)。


@DrPizza:我不是性能专家,但我希望定点运算比浮点运算更快(在大多数情况下)。

@克雷格·H:好的。我完全可以打印它。如果a或b存钱,则应以定点表示。我正在努力思考一个真实的例子,在这种例子中,这种逻辑应该与浮点数相关联。适用于彩车的东西:

  • 重量
  • 等级
  • 距离
  • 实际值(例如来自ADC的值)

对于所有这些事情,您要么编号然后简单地将结果呈现给用户以供人工解释,要么做出比较性的陈述(即使这样的陈述是"该事物与另一事物之差在0.001之内")。像mine这样的比较语句仅在算法上下文中有用:" 0.001之内"部分取决于您要询问的物理问题。那我0.02。还是我应该说2/100分?


If you are looking for two floats to be equal, then they should be identically equal in my opinion. If you are facing a floating point rounding problem, perhaps a fixed point representation would suit your problem better.

也许我应该更好地解释这个问题。在C ++中,以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>

using namespace std;


int main()
{
  float a = 1.0;
  float b = 0.0;

  for(int i=0;i<10;++i)
  {
    b+=0.1;
  }

  if(a != b)
  {
    cout <<"Something is wrong" << endl;
  }

  return 1;
}

打印短语"出了点问题"。您是说应该吗?


If you are looking for two floats to be equal, then they should be identically equal in my opinion. If you are facing a floating point rounding problem, perhaps a fixed point representation would suit your problem better.

也许我们无法承受这种方法会造成的范围或性能损失。


如果您希望两个浮点数相等,那么我认为它们应该相等。如果您面临浮点取整问题,那么定点表示可能会更适合您的问题。


If you have floating point errors you have even more problems than this. Although I guess that is up to personal perspective.

即使我们进行数值分析以最大程度地减少误差的累积,我们也无法消除它,而剩下的结果应该是相同的(如果我们使用实数进行计算)但有所不同(因为我们不能使用实数进行计算)。


This seems to take care of most problems by combining relative and absolute error tolerance. Is the ULP approach better? If so, why?

ULP是两个浮点数之间"距离"的直接度量。这意味着它们不需要您想出相对和绝对误差值,也不必确保获得"大约正确"的值。使用ULP,您可以直接表达您想要的数字接近程度,并且相同的阈值对于小数值和大数值都适用。


it's the best way to do it that I've come across, insofar as it provides the most robust comparisons even in the face of floating point errors.

如果您有浮点错误,那么您将遇到的问题甚至更多。虽然我认为这取决于个人观点。


Oh dear lord please don't interpret the float bits as ints unless you're running on a P6 or earlier.

即使它导致它通过内存从向量寄存器复制到整数寄存器,甚至使流水线停顿,它也是我遇到的最好的方法,因为即使面对现实,它也提供了最可靠的比较浮点错误。

即是值得付出的代价。


噢,亲爱的上帝,除非您在P6或更早的版本上运行,否则请勿将浮点位解释为整数。


推荐阅读