【问题标题】:Floating point equality for caching expensive computations用于缓存昂贵计算的浮点等式
【发布时间】:2017-02-14 12:23:31
【问题描述】:

关于预期由不同计算产生的两个浮点数完全相等的危险,已经有很多问题和答案,因为浮点数不是实数。这个问题不是关于取决于相等性检查的正确性,而是关于基于它的缓存。

想象一下你有这个代码:

if(myfloat != _last_float) {
    refresh_expensive_computation(myfloat);
    _last_float = myfloat;
}

在这种情况下,相等比较的存在纯粹是为了防止做多余的工作。如果输入不变,我们将避免再次进行昂贵的计算(我们假设昂贵的函数是确定性的,并且没有其他输入发生变化)。

如果两者真的相等(这意味着如果我们可以用实数而不是浮点数计算,它们就会相等)但被错误地检测到不相等,在最坏的情况下,我们会冗余地进行昂贵的计算,但我们的程序的答案是仍然正确。 AFAIK 如果计算是在比浮点数的内存表示更宽的寄存器中完成的(例如,在启用 80 位 fp 寄存器时在 32 位 x86 上),它们只能错误地比较相等,并且在转换为内存表示后它们会发生两者按位相等。在这种情况下,差异必须超出内存表示的精度,对于对我来说很重要的比较,它必须低于 epsilon,因为否则我会使用更广泛的类型,比如 double。

所以我要断言这种使用浮点相等是安全的。所以第一个问题是,我错了吗?

其次,如果我们假设它是安全的,我想避免错误地返回 true,因为这会导致计算成本高昂。在寄存器比内存表示更宽的机器上避免这种情况的一种方法是使用 memcmp 强制它比较内存表示(NaN 的语义不会完全相同,现在它将与完全相同的按位实例进行比较本身,但对于缓存这是一种改进,或者对于 +0 和 -0 但可能是特殊情况)。但是,该 memcmp 将比寄存器中的浮点比较慢。有没有办法检测平台何时具有更宽的寄存器,以便我可以 #ifdef 或类似方法在安全的平台上获得优化的实现?

【问题讨论】:

  • 你怎么知道缓存的值是否正确,而无需进行计算以找出它应该是什么?
  • 对不起,缓存的浮点数应该被称为最后一个浮点数,编辑得更清楚。我们正在查看输入是否正在改变。我们假设相同的输入产生相同的输出。
  • 好的...如果您正在保存输入/输出对,并在新输入与保存的输入匹配时使用保存的输出值,那么只要只有一个输出值就可以了对给定的输入是有效的......不过,这似乎很明显,所以我很惊讶你会问。

标签: c++ c floating-point floating-point-comparison


【解决方案1】:

大多数memcmp 实现都对寄存器进行了小值优化,因此使用它应该没问题。但是,如果您不想依赖它,也可以执行reinterpret_cast<int>() 之类的操作。如果您想更加安全并使用包含此类命令的库集,请添加 compile_assert(sizeof(int) == sizeof(float))

请注意 NaN。 NaN 不等于任何东西,甚至是另一个 NaN。如果您像这样比较内存,它们将显示为相等,这听起来像是您想要的,但是您可能需要添加额外的代码以确保所有 NaN 都被同等对待。

【讨论】:

  • 有趣,没想到小值优化。在我的应用程序中,实际上成本似乎是发送到 memcmp。我必须比较重新解释演员的生成代码。
【解决方案2】:

(C99) 为避免某些更高精度的 FP 数学提供不精确比较,请使用 volatile 强制计算使用最新的 float 值。

if ((volatile float) myfloat != (volatile float) _last_float) {
  refresh_expensive_computation(myfloat);
  _last_float = myfloat;
}

注意:使用_ 作为前导字符,然后保留一个字母作为变量名。最好重命名_last_float

注意:-0.0f 等于 +0.0f。如果这些具有相同值的不同floats 很重要,则需要除!= 之外的其他代码。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-28
    • 2011-03-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多