9

I have an algorithm which uses floats or doubles to perform some calculations.

Example:

double a;
double b;
double c;
...
double result = c / (b - a);
if ((result > 0) && (result < small_number))
{
    // result is relevant...
} else {
    // result not required...
}

Now, I am worried about (b - a) might be zero. If it is close to zero but not zero, it does not matter because the result will be out of range to be useful, and I already detect that (as (b - a) approaches zero, result will approach +/- inf, which is not in the range 0-small_number...)

But if the result of (b - a) is exactly zero, I expect that something platform dependant will happen due to divide by zero. I could change the if statement to:

if ((!((b-a) == 0.0)) && ((result = c/(b-a)) > 0) && (result < small_number)) {

but I don't know if (b-a) == 0.0 will always detect equality with zero. I have seen there are multiple representations for exact zero in floating point? How can you test for them all without doing some epsilon check, which I don't need (a small epsilon will be ignored in my algorithm)?

What is the platform independant way to check?

EDIT:

Not sure if it was clear enough to people. Basically I want to know how to find if an expression like:

double result = numerator / denominator;

will result in a floating point exception, a cpu exception, a signal from the operating system or something else.... without actually performing the operating and seeing if it will "throw"... because detecting a "throw" of this nature seems to be complicated and platform specific.

Is ( (denominator==0.0) || (denominator==-0.0) ) ? "Will 'throw'" : "Won't 'throw'"; enough?

Bingo
  • 3,785
  • 6
  • 23
  • 27
  • 1
    Even if `b - a` isn't exactly zero, the operation `c / (b - a)` could still overflow and send the value to `+/-INF`. – Mysticial Mar 03 '12 at 01:02
  • @Mystical That's fine, it would put the result outside of the range of interest for my problem. – Bingo Mar 03 '12 at 01:04
  • 8
    It's hard to find an implementation that doesn't use IEEE floating-point. So I think it's safe to assume that division by `0` will just yield `+INF` or `-INF`? Someone care to correct me if I'm wrong? – Mysticial Mar 03 '12 at 01:06
  • 2
    @Mysticial: Depends on what the numerator is. Apparently `0.0 / 0.0` is NaN, and triggers a different FP exception when exceptions are enabled. – cHao Mar 03 '12 at 01:28
  • Ah, good point. I didn't think about that - especially the `0.0 / 0.0` case. – Mysticial Mar 03 '12 at 01:29
  • BTW, Mystical is partially correct. gcc will simply do a segmentation fault, which is (I believe) an un-catchable exception. At any rate, Division by zero should never be a caught exception. I think this is an excellent question and IMHO, any person writing a division operator should always test for zero regardless of the code – eggmatters Jan 29 '13 at 08:22
  • 2
    Try rearranging the condition so that you're dividing by `small_number` instead of `b-a`. – Mark Ransom May 20 '13 at 15:37

8 Answers8

10

It depends on how b and a got their values. Zero has an exact representation in floating point format, but the bigger problem would be almost-but-not-quite zero values. It would always be safe to check:

if (abs(b-a) > 0.00000001  && ...

Where 0.00000001 is whatever value makes sense.

wallyk
  • 56,922
  • 16
  • 83
  • 148
  • 5
    Why exactly would an almost-but-not-quite zero number be a problem? My algorithm already rejects results from a almost-but-not-quite zero denominator... – Bingo Mar 03 '12 at 01:07
  • @Bingo: I don't see your algorithm rejecting `b - a` with a value of `1e-300`. Such a test costs little and is great insurance for resisting morphing of code, even if one version of it cannot produce that value. – wallyk Mar 03 '12 at 01:19
  • 1
    the point is that anything divided by nearly zero is large... outside the range of `0` to `small_number` which is the range I am interested in. – Bingo Mar 03 '12 at 01:50
  • 1
    Shouldn't that be fabs instead of abs? – daisy Sep 23 '12 at 10:54
  • @warl0ck: if you want to use the function version, that would be okay. However, there is the macro/template version `abs()` which works with all scalar types. – wallyk Sep 25 '12 at 22:56
  • 2
    This answer helped me in my search. Was dubious about abs vs. fabs and in gcc, abs works on all scalar types (I tested it on qreal and glFloat as well.) My own two cents are inlining the test with a ternary (As long as you're the maintainer or you comment a lot) `double result = c / ((abs((b - a)) > 0.00000001f) ? (b - a) : 0.00000001f) ;` That expression is, however, ugly as sin. – eggmatters Jan 29 '13 at 08:15
6

Here's how you do it: instead of checking for (result < small_number), you check for

(abs(c) < abs(b - a) * small_number)

Then all your troubles disappear! The computation of c/(b-a) will never overflow if this test is passed.

TonyK
  • 16,761
  • 4
  • 37
  • 72
2

I guess you can use fpclassify(-0.0) == FP_ZERO . But this is only useful if you want to check if someone did put some kind of zero into float-type variable. As many already said if you want to check result of calculation you may get values very close to zero due to nature of representation.

ony
  • 12,457
  • 1
  • 33
  • 41
  • Please comment a reason for downvoting. I promise that's only for understanding my mistake. – ony Dec 21 '15 at 04:42
2

In brief, we can know a floating number is ZERO exactly if we know it represent format.


In practice, we compare x with a small number. And if x is less than this number, we think x is as same as ZERO functionally (but most of time our small number is still large than zero). This method is very easy, efficient and can cross platform.


Actually, the float and double have been presented by special format, and the widely used one is IEEE 754 in current hardware which divided the number into sign, exponent and mantissa (significand) bits.

So, if we want to check if a float number is ZERO exactly, we can check if both exponent and mantissa is ZERO, see here.

In IEEE 754 binary floating point numbers, zero values are represented by the biased exponent and significand both being zero. Negative zero has the sign bit set to one.

Take float for example, we can write a simple code to extract exponent and mantissa bit and then check it.

#include <stdio.h>   

typedef union {
          float f;
          struct {
              unsigned int mantissa : 23;
              unsigned int exponent : 8;
              unsigned int sign :     1;
          } parts;
} float_cast;


int isZero(float num) {

    int flag = 0;
    float_cast data;
    data.f = num;

    // Check both exponent and mantissa parts
    if(data.parts.exponent == 0u && data.parts.mantissa == 0u) {
       flag = 1;
    } else {
       flag = 0;
   }

   return(flag);
}


int main() {

   float num1 = 0.f, num2 = -0.f, num3 = 1.2f;

   printf("\n is zero of %f -> %d", num1, isZero(num1));
   printf("\n is zero of %f -> %d", num2, isZero(num2));
   printf("\n is zero of %f -> %d", num3, isZero(num3));

   return(0);
}

Test results:

# is zero of 0.000000 -> 1
# is zero of -0.000000 -> 1
# is zero of 1.200000 -> 0


More examples:

Let's check when the float becomes real ZERO with code.

void test() {
     int i =0;
     float e = 1.f, small = 1.f;
     for(i = 0; i < 40; i++) {
         e *= 10.f;
         small = 1.f/e;
         printf("\nis %e zero? : %d", small, isZero(small));
     }
     return;
}


is 1.0000e-01 zero? : NO
is 1.0000e-02 zero? : NO
is 1.0000e-03 zero? : NO
is 1.0000e-04 zero? : NO
is 1.0000e-05 zero? : NO
is 1.0000e-06 zero? : NO
is 1.0000e-07 zero? : NO
is 1.0000e-08 zero? : NO
is 1.0000e-09 zero? : NO
is 1.0000e-10 zero? : NO
is 1.0000e-11 zero? : NO
is 1.0000e-12 zero? : NO
is 1.0000e-13 zero? : NO
is 1.0000e-14 zero? : NO
is 1.0000e-15 zero? : NO
is 1.0000e-16 zero? : NO
is 1.0000e-17 zero? : NO
is 1.0000e-18 zero? : NO
is 1.0000e-19 zero? : NO
is 1.0000e-20 zero? : NO
is 1.0000e-21 zero? : NO
is 1.0000e-22 zero? : NO
is 1.0000e-23 zero? : NO
is 1.0000e-24 zero? : NO
is 1.0000e-25 zero? : NO
is 1.0000e-26 zero? : NO
is 1.0000e-27 zero? : NO
is 1.0000e-28 zero? : NO
is 1.0000e-29 zero? : NO
is 1.0000e-30 zero? : NO
is 1.0000e-31 zero? : NO
is 1.0000e-32 zero? : NO
is 1.0000e-33 zero? : NO
is 1.0000e-34 zero? : NO
is 1.0000e-35 zero? : NO
is 1.0000e-36 zero? : NO
is 1.0000e-37 zero? : NO
is 1.0000e-38 zero? : NO
is 0.0000e+00 zero? : YES   <-- 1e-39
is 0.0000e+00 zero? : YES   <-- 1e-40
Patric
  • 2,063
  • 17
  • 18
1

UPDATE (2016-01-04)

I've received some downvotes on this answer, and I wondered if I should just delete it. It seems the consensus (https://meta.stackexchange.com/questions/146403/should-i-delete-my-answers) is that deleting answers should only be done in extreme cases.

So, my answer is wrong. But I guess I'm leaving it up because it provides for an interesting "think out of the box" kind of thought experiment.

===============

Bingo,

You say you want to know if b-a == 0.

Another way of looking at this is to determine whether a == b. If a equals b, then b-a will be equal 0.

Another interesting idea I found:

http://www.cygnus-software.com/papers/comparingfloats/Comparing%20floating%20point%20numbers.htm

Essentially, you take the floating point variables you have and tell the compiler to reinterpret them (bit for bit) as signed integers, as in the following:

if (*(int*)&b == *(int*)&a)

Then you are comparing integers, and not floating points. Maybe that will help? Maybe not. Good luck!

Community
  • 1
  • 1
Aaron Johnson
  • 795
  • 1
  • 8
  • 16
  • 2
    I'd upvote this if it weren't for the rather ugly casting thing. `if (a!=b) { /* do the math */ }` is good enough for this case; anything else is overkill. – cHao Mar 03 '12 at 01:13
  • Thanks for your answer. The equality idea is worth thinking about. Would be nice for a more general solution when the denominator wasn't the result of a subtraction though. Also, I think comparing bitwise (which is the integer approach) is the problem here. Different representations of zero would have different bit patterns anyway. – Bingo Mar 03 '12 at 01:13
  • 3
    Not only `*(int*)&b == *(int*)&a` breaks strict aliasing rules, but it makes `+0.` appear different from `-0.`. I do not see any point to using this comparison here. – Pascal Cuoq May 20 '13 at 16:07
  • 1
    Even worse, on nearly all platforms, `int` is 32 bits and `double` 64, so you're just comparing the high or low (depending on your platform) 32 bits for equality, which is useless. (Plus, it would compare two NaNs as equal if they happen to have the same bits set, and unequal if they don't, which is definitely not useful…) – abarnert Sep 13 '13 at 17:28
0

I believe that (b-a)==0 will be true exactly in those cases when the c/(b-a) would fail because of (b-a) being zero. The float maths is tricky but questioning this is exaggerating in my opinion. Also I believe that the (b-a)==0 is going to be equivalent to b!=a.

Distinguishing positive and negative 0 is also not necessary. See e.g. here Does float have a negative zero? (-0f)

Community
  • 1
  • 1
David L.
  • 3,149
  • 2
  • 26
  • 28
  • Regarding “the order of evaluation of the sub-expressions in your condition is not defined in c++”, in `e1 && e2`, the order of evaluation **is** defined. `e2` is always evaluated after `e1` and only if `e1` evaluates to true/nonzero. – Pascal Cuoq May 20 '13 at 16:10
  • You're right! http://stackoverflow.com/questions/7112282/order-of-evaluation-of-operands I will delete that part. Rest of the answer I leave although the deleted part was why I decided to reply... – David L. May 20 '13 at 19:13
  • Dividing with floating point zero is well defined if the compiler follows IEEE standards. [Read more on SO](http://stackoverflow.com/questions/12617408/a-few-things-about-division-by-zero-in-c) or read [the IEEE floating point standard](http://en.wikipedia.org/wiki/Division_by_zero#In_computer_arithmetic)! – DanielTuzes Aug 25 '13 at 22:51
0

For epsilon, in there is a standard template definition std::numeric_limits::epsilon(). I guess checking the difference to be bigger than std::numeric_limits::epsilon() should be safe enough to protect against division by zero. No platform dependency here I guess.

mcjoan
  • 91
  • 1
  • 2
  • This answer is nonsense. `std::numeric_limits::epsilon()` is not intended for this usage, it measures the ULP just above `1.0`. And when dividing by `x`, division by zero only occurs when `x == 0` (overflow can occur, but this has already been pointed out). – Pascal Cuoq Sep 13 '13 at 17:44
  • Right, it is not an answer to the original problem, just a remark related to epsilon. I should have put it in as a comment, but many previous posts referred to this. But your remark is correct. More on this [http://en.cppreference.com/w/cpp/types/numeric_limits/epsilon] – mcjoan Nov 30 '13 at 18:30
-1

You could try

if ((b-a)!=(a-b) && ((result = c/(b-a)) > 0) && (result < small_number))) {
...
Mark
  • 1,455
  • 3
  • 28
  • 51
  • Not exactly sure, but are you sure that `(b-a)` and `(a-b)` would produce the same version of zero in floating point? – Bingo Mar 03 '12 at 01:10