10

I have a number which might be zeros. I divide by that number so I want to test if it's zero to prevent NaN's and infinitys. Is it possible that I still create NaNs / infinity because of rounding errors within the division?

double x; // might be zero
double y;

if(x != 0) return y / x;

EDIT

Thanks for the responses. I'll add some subquestions then.

1) assuming neither x nor y is NaN / +inf or -inf, would a division that results in -inf / +inf result in more CPU cycles or any other unwanted behaviour? (Could it crash?)

2) is there a way to prevent the division from resulting in infinity? Using offsets and so on.

Richard Telford
  • 9,558
  • 6
  • 38
  • 51
ruhig brauner
  • 943
  • 1
  • 13
  • 35

3 Answers3

9

Can division by non-zero still create a nan / infinity

Yes.

If IEEE-754 is followed then:

  • If either operand is NaN, the result will be NaN.
  • If both numerator and denumerator are infinity, the result is NaN.
  • If only the numerator is infinity, the result is infinity.
  • If division by small denumerator (or large numerator) overflows, the result may be infinity, depending on current rounding mode.

Rules of other representations may be different.


2) is there a way to prevent the devision from resulting in infinity

This should go a long way in preventing that:

#include <cfenv>
#include <cassert>
#include <cmath>
#include <limits>

// ...

static_assert(std::numeric_limits<decltype(x)>::is_iec559, "Unknown floating point standard.");
#pragma STDC FENV_ACCESS ON
int failed = std::fesetround(FE_TOWARDZERO);
assert(!failed);
if(x != 0 && std::isfinite(x) && std::isfinite(y))
    return y / x;
else
    throw std::invalid_argument("informative message");

Some compilers may need non-default options to enable full IEEE 754 compliance (-frounding-math on GCC).

eerorika
  • 232,697
  • 12
  • 197
  • 326
  • "depending on current rounding mode" This is critical, see `fegetround`/`fesetround`. If you round toward zero, you can never get an inf if you divide by finite non-zero. – sbabbi Jul 25 '16 at 16:05
  • I feel like fesetround is nothing that should be called in a performance-sensitive function, right? – ruhig brauner Jul 25 '16 at 16:16
  • @ruhigbrauner possibly not. You may want to move it outside any hot loops. – eerorika Jul 25 '16 at 16:17
7

Dividing a very small number into a very large number, or multiplication of two very large numbers, may yield an "infinity". Dividing an infinity by another infinity will yield a NaN. For example, (1E300/1E-300)/(1E300/1E-300) or (1E300*1E300)/(1E300*1E300) will both yield NaN.

supercat
  • 77,689
  • 9
  • 166
  • 211
5

Yes, just look at code below

#include <iostream>
int main ()
{
    double x = 1, y = 2;
    while (y != 0) {
        std::cout << y << " " << x / y << std::endl;
        y /= 2;
    }
}

at some moment you will get:

8.9003e-308 1.12356e+307
4.45015e-308 2.24712e+307
2.22507e-308 4.49423e+307
1.11254e-308 8.98847e+307
5.56268e-309 inf
2.78134e-309 inf
1.39067e-309 inf
6.95336e-310 inf
vadikrobot
  • 1,725
  • 1
  • 10
  • 8