【问题标题】:Comparing IEEE floats and doubles for equality比较 IEEE 浮点数和双精度数的相等性
【发布时间】:2008-08-21 21:47:24
【问题描述】:

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

【问题讨论】:

  • 查看此answer 到类似问题。

标签: variables floating-point inequality


【解决方案1】:

我认为最好的方法是比较ULPs

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;
}

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

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

【讨论】:

  • 这会在精确度方面胜出。但是为了性能......你必须牺牲一些准确性来提高速度。同意吗?
  • 如果您需要双精度或可移植性:我在 Google 测试中发现了一个很棒的跨平台实现,可以同时处理双精度和浮点数,并在此处发布:stackoverflow.com/questions/17333/…
【解决方案2】:

我目前使用的版本是这个

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 方法更好吗?如果有,为什么?

【讨论】:

    【解决方案3】:

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

    这取决于你对他们做什么。与 IEEE 浮点数具有相同范围的定点类型会慢很多倍(并且大很多倍)。

    适合花车的东西:

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

    【讨论】:

      【解决方案4】:

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

      此外,在 BLAS 中,您经常需要检查浮点数是否正好是零或一。例如,例程 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 编写的,但如果您将其他编程语言用于数值软件,则逻辑是相同的。

      【讨论】:

        【解决方案5】:

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

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

        即这是值得付出的代价。

        【讨论】:

        • 我怀疑(a==b || (a!=a &amp;&amp; b != b)) 在许多处理器上会比任何涉及 float-bits-to-int 转换的东西都要快,尽管需要像上面这样的表达式真的让我很痛苦。我想知道 Nan!=NaN 决定提供了多少好处,以及浪费代码和调试时间的成本是多少?
        【解决方案6】:

        这似乎通过结合相对和绝对容错来解决大多数问题。 ULP 方法更好吗?如果有,为什么?

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

        【讨论】:

          【解决方案7】:

          如果你有浮点错误,你会遇到比这更多的问题。虽然我想这取决于个人观点。

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

          【讨论】:

            【解决方案8】:

            如果您正在寻找两个浮点数相等,那么我认为它们应该相等。如果您面临浮点舍入问题,也许定点表示更适合您的问题。

            【讨论】:

            • 处理(几乎)相等是循环中的常见情况,当事情变得“足够接近”时您想停止但您不希望它们完全收敛。
            【解决方案9】:

            如果您正在寻找两个浮点数相等,那么在我看来它们应该相等。如果您面临浮点舍入问题,也许定点表示更适合您的问题。

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

            【讨论】:

              【解决方案10】:

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

              @Craig H:当然。我完全可以打印它。如果 a 或 b 存钱,那么它们应该以固定点表示。我正在努力想一个真实世界的例子,其中这种逻辑应该与浮点数结合。适合花车的东西:

              • 权重
              • 排名
              • 距离
              • 真实世界的值(例如来自 ADC)

              对于所有这些事情,要么您提供更多数字并将结果简单地呈现给用户以供人工解释,要么您做出比较陈述(即使这样的陈述是,“这件事与另一件事相差 0.001 以内” )。像我这样的比较陈述仅在算法的上下文中有用:“0.001 以内”部分取决于您提出的物理问题。那是我的 0.02。还是应该说 2/100?

              【讨论】:

                【解决方案11】:

                这取决于你是什么 和他们一起做。定点类型 与 IEEE 浮点数具有相同的范围 会慢很多倍(并且 大很多倍)。

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

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

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

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

                【讨论】:

                  【解决方案12】:

                  一个 int 让我表达 ~10^9 的值 (无论范围如何)似乎 适合我的任何情况 会关心他们中的两个人 平等的。如果这还不够,请使用 64 位操作系统,你有大约 10^19 不同的价值观。

                  我实际上已经达到了这个限制......我试图在模拟中兼顾 ps 时间和时钟周期时间,您可以轻松达到 10^10 个周期。不管我做什么,我很快就超出了 64 位整数的微小范围...... 10^19 并不像你想象的那么多,现在给我 128 位计算!

                  浮点数让​​我能够找到数学问题的解决方案,因为值在低端溢出了很多零。所以你基本上有一个小数点在数字中浮动而没有精度损失(与64位整数相比,我希望浮点尾数中允许的不同值的数量更有限,但迫切需要这个范围! )。

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

                  很烦人,最后我放弃了整个尝试,只依靠浮点数和 来完成工作。不完美,但适用于设想的用例。

                  【讨论】:

                    【解决方案13】:

                    如果您正在寻找两个浮点数相等,那么我认为它们应该相等。如果您面临浮点舍入问题,也许定点表示更适合您的问题。

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

                    #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;
                    }
                    

                    打印短语“有问题”。你是说应该吗?

                    【讨论】:

                    • 由于浮点精度和舍入误差,您几乎总是会得到 a != b。
                    【解决方案14】:

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

                    【讨论】:

                      【解决方案15】:

                      这是我遇到的最好的方法,因为它提供了最可靠的比较,即使面对浮点错误。

                      如果你有浮点错误,你会遇到比这更多的问题。虽然我想这取决于个人观点。

                      【讨论】:

                        猜你喜欢
                        • 1970-01-01
                        • 1970-01-01
                        • 2022-01-25
                        • 2014-04-25
                        • 2013-07-24
                        • 2018-02-23
                        • 1970-01-01
                        • 2015-10-22
                        • 2021-10-06
                        相关资源
                        最近更新 更多