跳过 IEEE 754 中的 "test for zero" 检查

Skipping "test for zero" checks in IEEE 754

我在一本关于计算机图形学的畅销书中读到过这个,

There are many numeric computations that become much simpler if the programmer takes advantage of the IEEE rules. For example, consider the expression:

a = 1 / (1/b + 1/c)

Such expressions arise with resistors and lenses. If divide-by-zero resulted in a program crash (as was true in many systems before IEEE floating-point), then two if statements would be required to check for small or zero values of b or c. Instead, with IEEE floating-point, if b or c is zero, we will get a zero value for a as desired.

但是 b=+0c=-0 的情况呢?然后a=1/inf-inf=nan。这本书关于这种优化是错误的,还是我误解了什么?看来我们仍然需要至少检查一次 b 和 c 的符号,而不是根本不检查。

编辑 评论中的一个建议是对 NaN 进行 post 检查。这是 "take advantage" 这些数字类型的惯用方式吗?

bool is_nan(float x) { return x != x; }

float optic_thing(float b, float c) {
  float result = 1.0f / (1.0f/b + 1.0f/c);
  if (is_nan(result)) result = 0;
  return result;
}

But what about the case where b=+0 and c=-0?

通过添加 0.0.

-0.0 转换为 +0.0 而无需分支
int main(void) {
  double b = +0.0;
  double c = -0.0;
  printf("%e\n", b);
  printf("%e\n", c);
  printf("%e\n", 1.0/(1.0/b + 1.0/c));
  b += 0.0;
  c += 0.0;
  printf("%e\n", 1.0/(1.0/b + 1.0/c));
  return 0;
}

输出

0.000000e+00
-0.000000e+00
nan
0.000000e+00

[编辑] 另一方面,0.0 附近但不在 0.0 附近的任何值都可能是数字伪像,而不是准确的 阻力 值。上面的 tiny 值对仍然有问题 b = DBL_TRUE_MIN; c = -b; 问题是 1.0/tiny --> Infinity+Infinity + -Infinity --> NAN。可以求助于使用更宽的浮点数进行商计算或缩小操作数。

  b += 0.0;
  c += 0.0;
  printf("%Le\n", 1.0/(1.0L/b + 1.0L/c));

  // Lose some precision, but we are talking about real resistors/lenses here.
  b = (float) b + 0.0f;
  c = (float) c + 0.0f;
  printf("%e\n", 1.0/(1.0/b + 1.0/c));