将 NaN 或 Inf 与 0 相乘并得到 0

Multiply NaN or Inf with 0 and get 0

我有一组值a[i]

然后我计算

for(...){
  b[i] = log(a[i]);
}

那我总结一下

for(...){
  s += c[i] * b[i];
}

目前没问题。

但对于某些 i,我的 a[i] 可能为零并导致 b[i] = log(0) = -Inf。对于这些 ic[i] 也是零——这些是一些无效的数据点。但是zero*-Inf好像给了NaN,我的求和就乱了...

有没有办法在 c[i] = 0 时始终让 c[i] * b[i] = 0?

我看到的唯一方法是将全零 a[i] 设置为一个小的非零值或检查零,但可能有更好的解决方案。

我使用 C++ 和 std 数学函数,但我正在寻找一种尽可能通用的方法。

分配 b[i] 时,可以使用以下结构:

b[i] = (a[i] == 0) ? 0 : log(a[i]);

或者在浮点比较的情况下(阅读为什么这个解决方案也适用于当前问题的评论,但可能根本不是一个好主意):

b[i] = (fabs(a[i]) < DBL_EPSILON) ? 0 : log(a[i]);
for (...) {
  s += c[i] * (std::isinf(b[i]) ? 1 : b[i]);
}

for (...) {
  s += (c[i] == 0 ? 0 : c[i] * b[i]);
}

你可以在cmath中使用这个数字无穷大的测试;类似于:

s += (c[i] == 0 && std::isinf(b[i])) ? 0 : c[i] * b[i];

简而言之:

for(...){
  double tmp = c[i] * b[i];
  s += (tmp == tmp) ? tmp : 0;
}

0 * Inf 根据定义(IEEE 754 标准)是 NaN - 因此您无法更改该行为。

测试一个数是否为nan的'textbook'方法是与自身比较,例如:

if (x != x)
    std::cout << "x is nan" << std::endl;
else
    std::cout << "x is not nan" << std::endl;

这依赖于 NaN 不等于任何东西,包括它自己。 (再次根据定义)。

C++11 引入了更具可读性的 is_nan,如果您没有 C++11,我建议您自己编写

bool isnan(double arg) { return arg != arg; }

事实上,NaN 不与任何东西相比较,所以以下都有效:

if (x < y) std::cout << "x is not nan" << std::endl;
if (x > y) std::cout << "x is not nan" << std::endl;
if (x <= y) std::cout << "x is not nan" << std::endl;
if (x >= y) std::cout << "x is not nan" << std::endl;

这种令人惊讶的行为 (see this question) 背后的原因是能够使用上述条件进行编码以过滤掉 NaN 使得代码非常简单,也使得 NaN not setunknown 值的合适标记。