零防止除法:检查除数的表达式不会导致零与检查除数不为零?

Max*_*Max 7 c++ floating-point floating-accuracy divide-by-zero floating-point-precision

由于减法中的浮点误差,在以下情况下是否可以除零?

float x, y, z;
...
if (y != 1.0)
    z = x / (y - 1.0);
Run Code Online (Sandbox Code Playgroud)

换句话说,以下是否更安全?

float divisor = y - 1.0;
if (divisor != 0.0)
    z = x / divisor;
Run Code Online (Sandbox Code Playgroud)

Ste*_*non 6

假设IEEE-754浮点数,它们是等价的.

这是FP算法的基本定理,对于有限x和y,x - y == 0当且仅当x == y时,假设逐渐下溢.

如果将次正规结果刷新为零(而不是逐渐下溢),则该定理仅在结果x-y正常时才成立.因为1.0被很好地缩放,y - 1.0所以永远不是正常的,因此y - 1.0当且仅当y恰好是1.0时才是零,无论如何处理下溢.

当然,C++并不保证IEEE-754,但对于大多数"合理的"浮点系统来说,这个定理都是正确的.


Dav*_*own 5

这样可以防止你将其精确地除以零,但这并不意味着仍然不会因此而结束+/-inf.分母可能仍然足够小,所以答案是不能代表a double,你最终会得到一个inf.例如:

#include <iostream>
#include <limits>

int main(int argc, char const *argv[])
{
    double small = std::numeric_limits<double>::epsilon();
    double large = std::numeric_limits<double>::max() / small;
    std::cout << "small: " << small << std::endl;
    std::cout << "large: " << large << std::endl;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

在这个程序中small是非零的,但它是如此之小,large超过的范围double和是inf.

  • `y - 1`不能是`denorm_min()`,它最小的是`-epsilon()/ 2`,它不会是无穷大. (2认同)