用于缓存昂贵计算的浮点相等性

时间:2016-10-05 22:04:49

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

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

想象一下,你有这段代码:

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

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

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

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

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

2 个答案:

答案 0 :(得分:2)

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

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

答案 1 :(得分:0)

(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。如果具有相同值的这些不同float很重要,则需要其他代码而不是!=