简体   繁体   English

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

[英]Floating point equality for caching expensive computations

There are already a lot of questions and answers about the dangers of expecting two floats produced by separate computations to be exactly equal, because floating point numbers are not real numbers.已经有很多关于期望由单独计算产生的两个浮点数完全相等的危险的问题和答案,因为浮点数不是实数。 This question is not about correctness contingent on equality checking, it's about caching based on it.这个问题不是关于基于相等性检查的正确性,而是关于基于它的缓存。

Imagine you have this code:想象一下你有这样的代码:

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

In this instance the equality comparison purely exists to prevent doing redundant work.在这种情况下,相等比较纯粹是为了防止做多余的工作。 We are avoiding doing the expensive computation again if its input is unchanged (we assume the expensive function is deterministic and no other inputs to it have changed).如果输入不变,我们将避免再次进行昂贵的计算(我们假设昂贵的函数是确定性的,并且没有其他输入发生变化)。

In the event the two are really equal (meaning they would be if we could compute with reals instead of floating point) but are mistakenly detected not to be, in the worst case we do the expensive computation redundantly but our program's answer is still correct.如果两者真的相等(意味着如果我们可以用实数而不是浮点数来计算它们会是)但被错误地检测到不是,在最坏的情况下,我们冗余地进行了昂贵的计算,但我们的程序的答案仍然是正确的。 AFAIK they can only mistakenly compare equal if the computation was done in a register that is wider than the memory representation of float (eg on 32 bit x86 when the 80 bit fp registers are enabled), and after getting converted to the memory representation they happen to both be bitwise equal. AFAIK 他们只能错误地比较相等,如果计算是在比浮点数的内存表示更宽的寄存器中完成的(例如,当启用 80 位 fp 寄存器时,在 32 位 x86 上),并且在转换为内存表示后,它们会发生两者按位相等。 In that case the difference must be beyond the memory representation's precision, which must be below the epsilon for comparisons that matter to me, because otherwise I'd be using a wider type like double.在这种情况下,差异必须超出内存表示的精度,对于对我来说很重要的比较,它必须低于 epsilon,否则我将使用更宽的类型,如 double。

So I'm going to assert this use of floating point equality is safe.所以我要断言这种浮点相等的使用是安全的。 So the first question is, am I wrong?所以第一个问题是,我错了吗?

Secondly, if we assume it's safe, I'd like to avoid mistakenly returning true because it causes a costly computation.其次,如果我们假设它是安全的,我想避免错误地返回 true,因为它会导致昂贵的计算。 One way to avoid that on machines with wider registers than memory representations would be to use memcmp to force it to compare memory representations (the semantics won't be exactly the same for NaN, which will now compare true against the exactly identical bitwise instance of itself, but for caching that's an improvement, or for +0 and -0 but that could be special cased).在具有比内存表示更宽的寄存器的机器上避免这种情况的一种方法是使用 memcmp 强制它比较内存表示(NaN 的语义不会完全相同,现在将与完全相同的按位实例进行比较本身,但对于缓存来说这是一种改进,或者对于 +0 和 -0 但这可能是特殊情况)。 However that memcmp will be slower than a floating point comparison in registers.然而, memcmp 会比寄存器中的浮点比较慢。 Is there a way to detect when a platform has wider registers, so I can #ifdef or similar to get the optimized implementation on platforms where that is safe?有没有办法检测平台何时具有更宽的寄存器,以便我可以 #ifdef 或类似方法在安全的平台上获得优化的实现?

Most memcmp implementations have small-value optimizations for registers, so it should be fine to use that.大多数memcmp实现都对寄存器进行了小值优化,因此使用它应该没问题。 However, if you don't want to rely on that you could also do something like reinterpret_cast<int>() .但是,如果您不想依赖它,您也可以执行类似reinterpret_cast<int>() Add a compile_assert(sizeof(int) == sizeof(float)) if you want to be extra safe and are using a library set that includes such a command.如果您想更加安全并使用包含此类命令的库集,请添加compile_assert(sizeof(int) == sizeof(float))

Do watch out about NaN.请注意 NaN。 NaN is not equal to anything, even another NaN. NaN 不等于任何东西,甚至是另一个 NaN。 If you compare memory like this they will show up as equal, which is what it sounds like you want, however you may want to add additional code to make sure all NaNs are treated the same.如果您像这样比较内存,它们将显示为相等,这听起来像是您想要的,但是您可能需要添加额外的代码以确保所有 NaN 都得到相同的处理。

(C99) To avoid some higher precision FP math from providing a not exact compare, use volatile to force the computation to use the most recent float values. (C99) 为避免某些更高精度的 FP 数学提供不精确的比较,请使用volatile强制计算使用最新的浮点值。

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

Note: using _ as a leading character and then a letter as a variable name is reserved.注意:使用_作为前导字符,然后一个字母作为变量名是保留的。 Better to rename _last_float .最好重命名_last_float

Note: -0.0f equal +0.0f.注意:-0.0f 等于 +0.0f。 If these different float s that have the same value, are important, other code is needed than != .如果这些具有相同值的不同float很重要,则需要除!=之外的其他代码。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM