c语言 - 跳过 IEEE 754 中的"test for zero"检查



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

如果程序员利用了IEEE规则,许多数值计算就会变得简单得多。例如,考虑表达式: a = 1/(1/b + 1/c)

这种表达式出现在电阻和透镜中。如果除以零导致程序崩溃(在IEEE浮点之前的许多系统中都是如此),则需要两个If语句来检查b或c的小值或零值。相反,使用IEEE浮点,如果b或c为零,我们将得到所需的a的零值。

但是b=+0c=-0的情况呢?然后是a=1/inf-inf=nan。是书中关于优化的内容有误,还是我误解了什么?看来我们还需要至少检查一次b &C而不是根本没有检查。

编辑评论中的一个建议是对NaN进行事后检查。这是"利用"这些数字类型的惯用方法吗?

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;
}

但是b=+0c=-0的情况呢?

-0.0转换为+0.0,不添加0.0分支。

int main(void) {
  double b = +0.0;
  double c = -0.0;
  printf("%en", b);
  printf("%en", c);
  printf("%en", 1.0/(1.0/b + 1.0/c));
  b += 0.0;
  c += 0.0;
  printf("%en", 1.0/(1.0/b + 1.0/c));
  return 0;
}

输出
0.000000e+00
-0.000000e+00
nan
0.000000e+00

另一方面,任何接近0.0的值,而不是0.0,都可能是数字伪影,而不是准确的电阻值。上面的微小的值对如b = DBL_TRUE_MIN; c = -b;仍然有问题,问题是1.0/tiny -> Infinity+Infinity + -Infinity -> NAN。可以使用更宽的浮点数进行商计算,或者缩小操作数。
  b += 0.0;
  c += 0.0;
  printf("%Len", 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("%en", 1.0/(1.0/b + 1.0/c));

最新更新