0

Possible Duplicate:
Most effective way for float and double comparison

I have two values(floats) I am attempting to add together and average. The issue I have is that occasionally these values would add up to zero, thus not requiring them to be averaged.

The situation I am in specifically contains the values "-1" and "1", yet when added together I am given the value "-1.19209e-007" which is clearly not 0. Any information on this?

Community
  • 1
  • 1
Evan
  • 279
  • 1
  • 2
  • 11
  • 3
    You can never test floating point values for equality (as you do here, checking for equality with `0`). Googling (or even searching here) will produce lots of results. – Jon Oct 28 '11 at 16:25
  • 1
    Uhmmm (a - a) / 2, if a is 1, gives 0. Always. It cannot produce a floating point other than zero also if we put in account rounding error. are you sure this is the problem? – Salvatore Previti Oct 28 '11 at 16:28
  • 1
    @SalvatorePreviti: Incorrect. (a-a) will not always give 0 with floating point, due to the rounding that occurs when a floating point in a register is stored into memory. – Mooing Duck Oct 28 '11 at 16:31
  • @MooingDuck but surely `(1.+ -1.)` will always yield exactly zero, since `1.` and `-1.` are each exactly representable (typically). – Robᵩ Oct 28 '11 at 16:40
  • @Evan, are you certain that the values are `-1` and `1`, precisely? How did you come to store those values? Perhaps they are *very nearly*, but *not exactly* `-1` and `1`? – Robᵩ Oct 28 '11 at 16:41
  • 1
    @Rob the values are representing x coordinate values from a file that is being ingested. They are exactly -1 and 1 in this situation, but are stored in a float data type because they are not always going to be those values. – Evan Oct 28 '11 at 16:43
  • Generally, you cannot rely upon floating point numbers having exact values, but in this particular case you should be able to. Without seeing code, I don't know why `1.+ -1. == 0.` isn't `true` for you. FWIW, you might read [this](http://download.oracle.com/docs/cd/E19957-01/806-3568/ncg_goldberg.html). – Robᵩ Oct 28 '11 at 16:49
  • @MooingDuck `(a - a)` will always give `0`; the effect due to variable precision (register vs. memory) only comes into effect when `a` is a more or less complicated expression (typically a function call). – James Kanze Oct 28 '11 at 16:50
  • @JamesKanze: If `a` is a variable, then yes. I (right or wrong) interpreted `a` as an abstract expression that results in a float, which could be a variable, function call, equation, whatever. – Mooing Duck Oct 28 '11 at 16:57
  • @Mooing Duck: correct since the sign of a floating point number is just a bit, the higher bit. If i move a 32 bit float let's say in an 80 bit FPU register, both if it is negative or positive the register will contain the same absolute value: the conversion is the same. – Salvatore Previti Oct 28 '11 at 17:02
  • @MooingDuck Yes. The variable precision can cause some very unexpected results. It's hard to say what the real situation is, since the person asking the question simply said '1' and '-1'. – James Kanze Oct 28 '11 at 17:28

4 Answers4

4

I'm sorry but this doesn't make sense to me. Two floating point values, if they are exactly the same but with opposite sign, subtracted will produce always 0. This is how floating point operations works.

float a = 0.2f;
float b = -0.2f;
float f = (a - b) / 2;
printf("%f %d\n", f, f != 0); // will print out 0.0000 0

Will be always 0 also if the compiler doesn't optimize the code. There is not any kind of rounding error to take in account if a and b have the same value but opposite sign! That is, if the higher bit of a is 0 and the higher bit of b is 1 and all other bits are the same, the result cannot be other than 0.

But if a and b are slightly different, of course, the result can be non-zero.

One possible solution to avoid this can be using a tolerance...

float f = (a + b) / 2;

if (abs(f) < 0.000001f)
    f = 0;

We are using a simple tolerance to see if our value is near to zero.

A nice example code to show this is...

int main(int argc)
{
    for (int i = -10000000; i <= 10000000 * argc; ++i)
    {
        if (i != 0)
        {
            float a = 3.14159265f / i;
            float b = -a + (argc - 1);

            float f = (a + b) / 2;

            if (f != 0)
                printf("%f %d\n", a, f);
        }
    }
    printf("completed\n");
    return 0;
}

I'm using "argc" here as a trick to force the compiler to not optimize out our code.

Salvatore Previti
  • 8,956
  • 31
  • 37
  • They must be different somehow, but I cannot tell a difference between them. – Evan Oct 28 '11 at 16:45
  • That was confusing me too, since +1 and -1 can be exactly represented in a floating point format, the sums should be exactly 0, unless the 1 and -1 themselves weren't exact (e.g. if they were the sums of some fractional numbers). Oh well, the question is closed now, and the SO overlords wouldn't have tolerated a discussion of how these numbers originated anyway, since it wouldn't be in the form of an answer to the question asked =) – Sam Skuce Oct 28 '11 at 16:47
  • +1 + -1 = 0 :) using int, float, double. So i don't know why he said that :) – Salvatore Previti Oct 28 '11 at 16:50
  • 1
    @SamSkuce: It depends on how they get set. He said they're read from a file, which means they had to be assembled from characters, which means there was math involved, which means there's room for rounding errors. – Mooing Duck Oct 28 '11 at 17:06
2

At least right off, this sounds like typical floating point imprecision.

The usual way to deal with it is to round your numbers to the correct number of significant digits. In this case, your average would be -1.19209e-08 (i.e., 0.00000001192). To (say) six or seven significant digits, that is zero.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • This doesn't sound at all like typical floating point imprecision. In this case, both `-1` and `1` can be represented exactly, and adding them should give `0`. – James Kanze Oct 28 '11 at 16:48
  • @JamesKanze: If they truly are 1 and -1, that would be true. My guess (which I probably should have stated) was that the 1 and/or -1 was a result of some previous calculation which looked at mathematically should have resulted in exactly that number, but due to rounding only came close to it. – Jerry Coffin Oct 28 '11 at 16:49
  • these values are the min and max x coordinate values in a particular mesh. The values were ingested from a file and stored in a float array. Next I used the min/max functions to perform comparisons of all x values to determine the largest and smallest values. After attempting to average them I encountered this problem. – Evan Oct 28 '11 at 17:03
  • Evan: when you read floats from a stream, the values might not be exact, even if they can be exactly represented. – Mooing Duck Oct 28 '11 at 17:07
  • @MooingDuck If the value can be exactly represented, and the results when reading from a stream aren't exact, that's an error in the library. The algorithms for reading exactly are well know. – James Kanze Oct 28 '11 at 17:21
  • @Evan But were they truly `-1` and `1`? In other words, supposing IEEE, were all of the bits in the mantissa 0. When checking such things, it's important to set the output precision to 17 or more, in order to see exactly what you've got. (Setting output precision to 17 ensures that two different floating point values will always have a different representation.) – James Kanze Oct 28 '11 at 17:25
1

Takes the sum of all your numbers, divide by your count. Round off your answer to something reasonable before you do prints, reports comparisons, or whatever you're doing.

Stealth Rabbi
  • 10,156
  • 22
  • 100
  • 176
0

again, do some searching on this but here is the basic explanation ...

the computer approximates floating point numbers by base 2 instead of base 10. this means that , for example, 0.2 (when converted to binary) is actually 0.001100110011 ... on forever. since the computer cannot add these on forever, it must approximate it.

because of these approximations, we lose "precision" of calculations. hence "single" and "double" precision floating point numbers. this is why you never test for a float to be actually 0. instead, you test whether is below some threshhold which you want to use as zero.

drjrm3
  • 4,474
  • 10
  • 53
  • 91
  • Sorry, but testing floating point values for `0.0` is a valid and frequent operation. – James Kanze Oct 28 '11 at 16:45
  • it might be, but it really shouldn't be done. after much, if any, work on floating point numbers higher than simply adding or subtracting (which even then may cause problems) then "zero" can appear as almost any arbitrarily small number. – drjrm3 Oct 28 '11 at 18:25
  • Not if you know what you're doing. There are cases where comparing with `0.0` (or more generally, comparing using `==` and `!=`) are valid, and there are cases where it isn't. There is no absolute rule; you have to understand how machine floating point actually works, and judge in consequence. – James Kanze Oct 31 '11 at 09:40