Bingo
Bingo

Reputation: 3833

float/double equality with exact zero

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?

Upvotes: 9

Views: 39785

Answers (8)

Aaron Johnson
Aaron Johnson

Reputation: 815

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!

Upvotes: 1

Patric
Patric

Reputation: 2131

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

Upvotes: 2

ony
ony

Reputation: 13223

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.

Upvotes: 2

mcjoan
mcjoan

Reputation: 101

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.

Upvotes: 0

TonyK
TonyK

Reputation: 17114

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.

Upvotes: 6

David L.
David L.

Reputation: 3290

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)

Upvotes: 0

Mark
Mark

Reputation: 1537

You could try

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

Upvotes: -1

wallyk
wallyk

Reputation: 57764

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.

Upvotes: 10

Related Questions