2

I have a set of values a[i].

Then I compute

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

Then I sum up

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

That's no problem so far.

BUT for some i my a[i] may be zero and lead to b[i] = log(0) = -Inf. For these i, c[i] is also zero - those are some invalid data points. But zero*-Inf seems to give NaN, and my sum is messed up...

Is there a way to always have c[i] * b[i] = 0 when c[i] is = 0?

The only way I see is to set all zero a[i] to a small non-zero value or to check for zero, but there might be better solutions.

I use C++ and std math functions, but I'm looking for a method which works as generally as possible.

iikkoo
  • 2,810
  • 6
  • 33
  • 38
Michael
  • 7,407
  • 8
  • 41
  • 84

4 Answers4

5

In short:

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

0 * Inf is NaN by definition (IEEE 754 standard) - so you can't change that behaviour.

The 'textbook' way of testing if a number is nan is to compare with itself, eg:

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

This relies on the fact that NaN is not equal to anything, including itself. (again by definition).

C++11 introduces is_nan which is more readable, and if you don't have C++11 I'd recommend writing your own like

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

In fact, NaN does not compare true to anything, so the following will all work:

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;

The reason behind this surprising behavior (see this question) is that being able to code using the above conditions to filter out NaN makes for very straightforward code, and also makes NaN a suitable sentinel for not set or unknown values.

Community
  • 1
  • 1
Zero
  • 11,593
  • 9
  • 52
  • 70
4
for (...) {
  s += c[i] * (std::isinf(b[i]) ? 1 : b[i]);
}

for (...) {
  s += (c[i] == 0 ? 0 : c[i] * b[i]);
}
Mihai Morariu
  • 166
  • 1
  • 7
  • 2
    To fully answer the question, you need isinf, not isnan – galinette Oct 06 '15 at 11:58
  • You are right, I fixed that. And I also added another option which doesn't require isinf :-) – Mihai Morariu Oct 06 '15 at 11:59
  • The second option simply circumvent the problem. Nice. – ecotax Oct 06 '15 at 12:08
  • As long as c[i] is an integer, which I assume (although it's not explicitly stated) that it is. – Mihai Morariu Oct 06 '15 at 12:14
  • `c[i]` can be a float - no problem - so long as it is not the result of a calculation. (If it is result of a calculation is may be extremely close to zero but not quite equal, which may not be what is required). Since the OP stated `c[i] == 0` represents missing data, it seems likely the zero is read from a file (for example) or in some other way we can be sure it is exactly zero. – Zero Oct 06 '15 at 12:17
2

When you assign b[i] you can use the following construction:

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

Or in case of floating point comparison (Read the comments why this solution also works for current question, but may be not good idea at all):

b[i] = (fabs(a[i]) < DBL_EPSILON) ? 0 : log(a[i]);
maxteneff
  • 1,523
  • 12
  • 28
  • 1
    You may be misunderstanding what DBL_EPSILON is. In any case that comparison to DBL_EPSILON is a very bad idea. the original comparison to zero was fine. But for the original question, comparing `c[i]` to 0 made more sense than comparing `a[i]`, because when `a[i]==0 && c[i]!=0` the result can't be correct and you probably want NaN or INF. – JSF Oct 06 '15 at 12:08
  • 1
    This does what the questioner asked (`c[i] * b[i] = 0` when `c[i] is = 0`) but probably not what they want: as pointed out if `b[i]` is `nan` and `c[i]` should be zero but isn't due to rounding errors the sum will still be `nan`. Using `DBL_EPSILON` won't fix this, because the error in a floating point calculation may be arbitrarily large depending on the calculation. Note that if you _know_ `c[i]` will be zero (because it was set to zero, and not as a result of a calculation that may be only close to zero) this answer is fine. – Zero Oct 06 '15 at 12:09
2

You could use the test for a number being infinite in cmath; something like:

s += (c[i] == 0 && std::isinf(b[i])) ? 0 : c[i] * b[i];
ecotax
  • 1,933
  • 17
  • 22