Manipulating and Comparing Floating Points in Java

comparing float/double values using == operator

IBM has a recommendation for comparing two floats, using division rather than subtraction - this makes it easier to select an epsilon that works for all ranges of input.

if (abs(a/b - 1) < epsilon)

As for the value of epsilon, I would use 5.96e-08 as given in this Wikipedia table, or perhaps 2x that value.

Java equality of floats and doubles

When you write 4.4f, that results in the closest value representable as a float to the true value 4.4.

When you write 4.4, that results in the closest value representable as a double to the value 4.4.

When you write 4.4f == 4.4, that tests if the closest value representable as a float is still the closest value representable as a double to 4.4, which is false; double has more precision, and neither float nor double can represent 4.4 exactly.

Both float and double can represent 4.5 exactly, however; since they're using binary fractions, and 4.5 is an exact binary fraction, that works fine.

Is comparing two same literal float numbers for equality wrong?

There's a rule of thumb that you should apply to all programming rules of thumb (rule of thumbs?):

They are oversimplified, and will result in boneheaded decision making if pushed too far. IF you do not fully -grok- the intent behind the rule of thumb, you will mess up. Perhaps the rule of thumb remains a net positive (applying it without thought will improve things more than it will make them worse), but it will cause damage, and in any case it cannot be used as an argument in a debate.

So, with that in mind, clearly, there is no point in asking the question:

"Giving that the rule of thumb 'do not use == to compare floats' exists, is it ALWAYS bad?".

The answer is the extremely obvious: Duh, no. It's not ALWAYS bad, because rules of thumb pretty much by definition, if not by common sense, never ALWAYS apply.

So let's break it down then.

WHY is there a rule of thumb that you shouldn't == compare floats?

Your question suggests you already know this: It's because doing any math on floating points as represented by IEEE754 concepts such as java's double or float are inexact (vs. concepts like java's BigDecimal, which is exact *).

Do what you should always do when faced with a rule of thumb that, upon grokking why the rule of thumb exists and realizing it does not apply to your scenario: Completely ignore it.

Perhaps your question boils down to: I THINK I grok the rule of thumb, but perhaps I'm missing something; aside from the 'floating point math introduces small deviations which mess up == comparison', which does not apply to this case, are there any other reasons for this rule of thumb that I am not aware of?

In which case, my answer is: As far as I know, no.

*) But BigDecimal has its own equality problems, such as: Are two BigDecimal objects that represent the same mathematical number precisely, but which are configured to render at a different scale 'equal'? That depends on whether your viewpoint is that they are numbers or objects representing an exact decimal point number along with some meta properties including how to render it and how to round things if explicitly asked to do so. For what it is worth, the equals implementation of BD, which has to make a sophie's choice and choose between 2 equally valid interpretations of what equality means, chooses 'I represent a number', not 'I represent a number along with a bunch of metadata'. The same sophie's choice exists in all JPA/Hibernate stacks: Does a JPA object represent 'a row in the database' (thus equality being defined solely by the primary key value, and if not saved yet, two objects cannot be equal, not even to itself, unless the same reference identity), or does it represent the thing that the row represents, e.g. a student, and not 'a row in the DB that represents a student', in which case unid is the one field that does NOT matter for identity, and all the others (name, birthdate, social security number, etc) do. equality is hard.

Floating point comparison without any tolerance

Please note that there is no such thing as exactness in floating points due to the fundamental limitation of the binary code. You cannot express decimal in binary exactly, instead halves, quarters (and other ratios with denominator being a power of two) are used to express values that are 'close enough' within some acceptable tolerance. You cannot remove this tolerance and compare exact values, because otherwise you cannot express decimals in binary.

Useful link.

UPDATE:
Just found on the stackoverflow this great response, so that I don't have to write it all over. Read this and maybe you will consider using BigDecimal for the task at hand.

Comparing floats and doubles in Java

On the final line, where you compare a==b, the value of a is being implicitly promoted to double before the comparison. So, in effect, what is really being evaluated is (((double)a)==b) which, of course, evaluates true, since b was initialized by casting a as double in the first place.

In other words, the last boolean expression (a==b) asks:

is (some float value converted to a double)==(the same float value converted to a double somewhere else)

and the answer is : yes.

UPDATE: a commenter below made a good point: when a is promoted to double, the stored numeric value does not actually change, even though it appears to because a different value is printed. The issue is with how floating point numbers are stored; without making this answer WAAAY too long and wading in past my depth, what you need to know is that some simple decimal values cannot be perfectly represented in binary floating point form, no matter how many digits you have (just like you can't perfectly represent 1/3 in base 10 without an infinite number of 3s, as in 0.333333...), so when you assign one of these values using float (a 32-bit representation of a floating point number) and then you convert it to a double (a 64-bit representation) and then use println to display the value of that number, you may see more digits.

The println method displays the shortest string of decimal digits that convert to the same float (or double) value the computer sees internally. Although the same numerical value is passed as argument in the first two calls to println in your example, the double type has more precision so println needs to print more digits to show “the shortest string of decimal digits that would convert to (double)99999.99F”. If your example had been 9999.50, both numbers would have printed the same, because binary numbers can represent 1/2 just fine.

NOTE: I am not even close to an expert on floating point numbers; you should check out (http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html) if you want a deeper understanding of what is going on here.



Related Topics



Leave a reply



Submit