24

I know we can't compare 2 floating point values using ==. We can only compare they are within some interval of each other. I know

if(val == 0.512) 

is wrong due to errors inherent in floating point calculations and conversion to binary and should be

if (val in (0.512-epsilon, 0.512+epsilon)) 

But is 0 special? Can we compare floats exactly to 0? Or even that is incorrect? Particularly in the context of C# and Java?

double val = 0; val = getVal(); if(val == 0) 
5
  • 1
    mathematical pedantry: it's epsilon, not ephsilon. good question, though. Commented Mar 4, 2010 at 16:52
  • 8
    Of course you can compare two floating point values using exact equality. For certain restricted purposes, it's not what you want to do, but saying you "can't" do it is nonsense, pure and simple, and prevents developers from learning about the actual semantics of floating-point. Commented Mar 4, 2010 at 20:01
  • 1
    @Carl: Thank you for the Greek pedantry! edited the post. I am so used to writing (U+0395) ε, I didn't know the exact spelling! Commented Mar 5, 2010 at 13:16
  • @Stephen: Could you please explain with references? You may want to add your explanation as an answer. You seem to imply that exact equality should be the norm [with interval comparison "For certain restricted purposes"]. I hope you already know about "What Every Computer Scientist Should Know About Floating-Point Arithmetic" validlab.com/goldberg/paper.pdf Commented Mar 5, 2010 at 14:28
  • @user256717 It's a little late for this, but so you know: Stephen Cannon was on the committee that maintains the IEEE-754 standard, so yes, he knows what he's talking about. Commented Jun 21 at 20:46

9 Answers 9

16

Even though 0 has an exact representation, you can't rely on the result of a calculation using floats to be exactly 0. As you noted, this is due to floating point calculation and conversion issues.

So, you should test for 0 against your tolerance epsilon.

Sign up to request clarification or add additional context in comments.

2 Comments

Pretty much all the replies advised me against special casing 0. Thank you! Accepting first one [in default order].
@Fakrudeen: default order s by score first, then random.
14

You can compare to zero if you assigned that variable zero. If you get zero from eg. a subtraction you can still get a very small number close to zero. eg.: 0.1-0.1 may evalute to something like 1e-9.

Comments

12

Use the tolerance / ephsilon approach.

I just evaluated the following in Java, which mathematically results in zero:

1.0/5.0 + 1.0/5.0 - 1.0/10.0 - 1.0/10.0 - 1.0/10.0 - 1.0/10.0 

and actually got

2.7755575615628914E-17 

Comments

4

Because zero does have an exact representation, it is possible for a value to compare == to zero. If the variable you are testing was set by an assignment, or from a value typed in (like getVal in your example?), it could easily be zero. But if it was the result of a calculation, the chances of it being exactly zero are very small. This is made worse because ordinary decimal fractions like 0.2 do not have an exact representation in floating point. That's why it's best to use epsilon.

Comments

3

For comparison with err all you need to is.

// compare a and b with an ERR error. if (Math.abs(a - b) <= ERR) 

To compare with 0

// compare a and 0 with an ERR error. if (Math.abs(a) <= ERR) 

3 Comments

What is ERR ?
@Jess Your acceptable error e.g.static final double ERR = 1e-6
Oh, I thought it was some kind of acronym. Maybe rename to ErrorTolerance? Or ZeroTolerance would be funny :D !
2

I would still recommend following the tolerance idiom and not compare to zero exactly.

Comments

0

I don't think you can in general - the calculation that occurs in getVal() might logically result in zero but that dosn't mean it will return zero. If you explicilty return a zero to indicate some condition then the compare should always work but I don't think it would be best practice. I'd modify the function to return a status code and pass the value to be changed byref.

Comments

0

In any non-trivial situation, you should really only use the tolerance approach. As noted, zero comparison is only accurate when you actually assigned it to zero.

Without repeating what other have said, I just want to emphasize the fact that using the tolerance approach is more future proof. What you once think is a simple assignment may involve actual arithmetic later. Using a naked comparison makes it painfully obscure to debug at a later date.

Comments

0

If you're writing something like

if(val == 0.512) 

the question is not, "does 0.512 have an exact representation?". The more important question is, "How was val computed, and how much error might have crept in?"

If no error has crept in, you can use == just fine, even on values like this one:

double val = 0.512; if(val == 0.512) { expected case; } else { surprising case; } 

Under reasonable circumstances, this will take the expected case. Yes, it's true, 0.512 can't be represented exactly in binary, so val is likely to end up containing something more like 0.51200000000000001 — but then, in the if(val == 0.512) line, 0.512 can't be represented exactly there, either, so is likely to be converted to the same value, 0.51200000000000001, which will match.

In most cases, however, it's likely that some error has crept in. Here is a simple example:

double val = 1.; val /= 49; val *= 49; val -= 1; if(val == 0) { expected case; } else { surprising case; } 

This will usually hit the surprising case, even though, yes, 0.0 is exactly representable.

See also this question and this question.

Comments