in my java book it told me to not directly compare 2 different float(type) numbers when storing them in variables. Because it gives an approx of a number in the variable. Instead it suggested checking the absolute value of the difference and see if it equals 0. If it does they are the same. How is this helpful? What if I store 5 in variable a and 5 in variable b, how can they not be the same? And how does it help if I compare absolute value??
double a=5,b=5;
if (Math.abs(a-b)==0)
//run code
if (a==b)
//run code
I don't see at all why the above method would be more accurate? Since if 'a' is not equal to 'b' it wont matter if I use Math.abs.
I appreciate replies and thank you for your time.
I tried both methods.
Inaccuracy with comparisons using the == operator is caused by the way double values are stored in a computer's memory. We need to remember that there is an infinite number of values that must fit in limited memory space, usually 64 bits. As a result, we can't have an exact representation of most double values in our computers. They must be rounded to be saved.
Because of the rounding inaccuracy, interesting errors might occur:
double d1 = 0;
for (int i = 1; i <= 8; i++) {
d1 += 0.1;
}
double d2 = 0.1 * 8;
System.out.println(d1);
System.out.println(d2);
Both variables, d1 and d2, should equal 0.8. However, when we run the code above, we'll see the following results:
0.7999999999999999
0.8
In that case, comparing both values with the == operator would produce a wrong result. For this reason, we must use a more complex comparison algorithm.
If we want to have the best precision and control over the rounding mechanism, we can use java.math.BigDecimal class.
The recommended algorithm to compare double values in plain Java is a threshold comparison method. In this case, we need to check whether the difference between both numbers is within the specified tolerance, commonly called epsilon:
double epsilon = 0.000001d;
assertThat(Math.abs(d1 - d2) < epsilon).isTrue();
The smaller the epsilon's value, the greater the comparison accuracy. However, if we specify the tolerance value too small, we'll get the same false result as in the simple == comparison
The thing is, That statement you read in your java book just prevents you from some errors in future, that can be hardly debugged. Computers store decimals/floats as binary, thus not everything we can express as rational in decimal numbers can be expressed as rational in binary, so there's always something like 0.7 = 0.699999999998511. You may not see difference in those comparisons you use, but in real project where you may use much more variables, add and subtract from them, this difference may appear in very surprising place.
There is some classic question about floating numbers. You may see it in any language as well
Why does this code print a result of '7'?
I have following two BigDecimal objects.
BigDecimal one = new BigDecimal(3.0);
BigDecimal two = new BigDecimal(3.00);
System.out.println(one.scale());//0
System.out.println(two.scale());//0
System.out.println(one.equals(two));//true
I've read JavaDocs, but anywhere can't understand what is difference between equals and compareTo method. JavaDoc says that these objects isn't equal by equals method and result must be false, but result is true. I'm confused.
You need to use the String constructor to get the correct scales, because the BigDecimal(double) will get the small scale possible
translates a double into a BigDecimal which is the exact decimal representation of the double's binary floating-point value. The scale of the returned BigDecimal is the smallest value such that (10scale × val) is an integer.
More precision about the documentations :
BigDecimal.equals(Object)
Compares this BigDecimal with the specified Object for equality. Unlike compareTo, this method considers two BigDecimal objects equal only if they are equal in value and scale (thus 2.0 is not equal to 2.00 when compared by this method).
BigDecimal.compareTo(BigDecimal)
Compares this BigDecimal with the specified BigDecimal. Two BigDecimal objects that are equal in value but have a different scale (like 2.0 and 2.00) are considered equal by this method. This method is provided in preference to individual methods for each of the six boolean comparison operators (<, ==, >, >=, !=, <=). The suggested idiom for performing these comparisons is: (x.compareTo(y) 0), where is one of the six comparison operators.
You will find that the equals use the scale for the comparison, giving some "strange" result.
BigDecimal bd1 = new BigDecimal("2"); //scale 0
BigDecimal bd2 = new BigDecimal("2.00"); //scale 2
bd1.equals(bd2); //false
bd1.compareTo(bd2); //0 => which means equivalent
It's better to use compareTo for BigDecimal. This method will return a number greater than zero if a > b, 0 if a == b, and less than zero if a < b
Change new BigDecimal(3.0); to new BigDecimal("3.0"); and you will see the difference.
You used the constructor new BigDecimal(double val) with a double-literal which value is just 3 it doesn't matter if you write 3.0 or 3.0000.
Also be careful with this constructor since it represents the exact decimal representation of the double's binary floating-point value, therefor new BigDecimal(0.1) creates a BigDecimal with the value 0.1000000000000000055511151231257827021181583404541015625 (See JavaDoc)
Better (always) use the constructor new BigDecimal(String val).
This question already has answers here:
Why are floating point numbers inaccurate?
(5 answers)
BigDecimal compareTo not working as expected
(1 answer)
Closed 7 years ago.
Why does new BigDecimal("0.015").compareTo(new BigDecimal(0.015)) return -1?
If I expect those two to be equal, is there an alternative way to compare them?
Due to the imprecise nature of floating point arithmetic, they're not exactly equal
System.out.println(new BigDecimal(0.015));
displays
0.01499999999999999944488848768742172978818416595458984375
To expand on the answer from #Reimeus, the various constructors for BigDecimal accept different types of input. The floating point constructors, take a floating point as input, and due to the limitations of the way that floats/doubles are stored, these can only store accurately values that are a power of 2.
So, for example, 2⁻², or 0.25, can be represented exactly. 0.875 is (2⁻¹ + 2⁻² + 2⁻³), so it can also be represented accurately. So long as the number can be represented by a sum of powers, where the upper and lower power differ by no more than 53, then the number can be represented exactly. The vast majority of numbers don't fit this pattern!
In particular, 0.15 is not a power of two, nor is it the sum of a power of two, and so the representation is not accurate.
The string constructor on the other hand does store it accurately, by using a different format internally to store the number. Hence, when you compare the two, they compare as being different.
A double cannot exactly represent the value 0.015. The closest value it can represent in its 64 binary bits is 0.01499999999999999944488848768742172978818416595458984375. The constructor new BigDecimal(double) is designed to preserve the precise value of the double argument, which can never be exactly 0.015. Hence the result of your comparison.
However, if you display that double value, for example by:
System.out.println(0.01499999999999999944488848768742172978818416595458984375);
it outputs 0.015 – which hints at a workaround. Converting a double to a String chooses the shortest decimal representation needed to distinguish it from other possible double values.
Thus, if you create a BigDecimal from the double's String representation, it will have a value more as you expect. This comparison is true:
new BigDecimal(Double.toString(0.015)).equals(new BigDecimal("0.015"))
In fact, the method BigDecimal.valueOf(double) exists for exactly this purpose, so you can shorten the above to:
BigDecimal.valueOf(0.015).equals(new BigDecimal("0.015"))
You should use the new BigDecimal(double) constructor only if your purpose is to preserve the precise binary value of the argument. Otherwise, call BigDecimal.valueOf(double), whose documentation says:
This is generally the preferred way to convert a double (or float) into a BigDecimal.
Or, use a String if you can and avoid the subtleties of double entirely.
What actually happens here is this:
0.015 is a primitive double. Which means that as soon as you write it, it is already no longer 0.015, but rather 0.0149.... The compiler stores it as a binary representation in the bytecode.
BigDecimal is constructed to store exactly whatever is given to it. In this case, 0.0149...
BigDecimal is also able to parse Strings into exact representations. In this case "0.015" is parsed into exactly 0.015. Even though double cannot represent that number, BigDecimal can
Finally, when you compare them, you can see that they are not equal. Which makes sense.
Whenever using BigDecimal, be cautious of the previously used type. String, int, long will remain exact. float and double have the usual precision caveat.
I've just recently come across a behavior inside BigDecimal that I wasn't previously aware of. I've always used them as an alternative to double for areas where precision is important. For example in financial calculations.
However I recently came across this fact
new BigDecimal("1.0").equals(new BigDecimal("1")) == false
I have to admit I was surprised by this. I figure it is because the first has a scale of 1 while the second has a scale of 0, but still it seems counter-intuitive. I think the reason I've never run into it before is because we've always used fixed scale BigDecimals for financial calculations.
Checking the BigDecimal documentation I can see that says that compareTo() == 0 should be used to check for equality ignoring scale while that equals() compares both the value and the scale.
Are there any other similar gotchas I should be aware of when using BigDecimals with different scales?
There is a value and a scale of the BigDecimal. Both need to be equal for the BigDecimals to be equal. From the java docs . . .
Unlike compareTo, this method considers two BigDecimal objects equal
only if they are equal in value and scale (thus 2.0 is not equal to
2.00 when compared by this method).
https://docs.oracle.com/javase/7/docs/api/java/math/BigDecimal.html#equals(java.lang.Object)
According to JavaDoc of the equals():
Unlike compareTo, this method considers two BigDecimal objects equal only if they are equal in value and scale (thus 2.0 is not equal to 2.00 when compared by this method).
So equals() checks if the objects are exactly the same. compareTo() "only" compares their numeric value.
BigDecimal equals checks that the contents of the two BigDecimal object are the same. e.g. their toString() would be the same. Just as "1.0" and "1" are not equal, nor is new BigDecimal("1.0").equals(new BigDecimal("1")) as the unscaledValue() and getScale() are both different.
The catch is that while you knew that == wouldn't compare the contents and you might have been told .equals is the solution for String, it may not do what you intended for BigDecimal.
For compareTo it has to work on what is greater than or less than and since the values are neither greater than or less than they can only be equal (but not equals).
According to this java.sun page == is the equality comparison operator for floating point numbers in Java.
However, when I type this code:
if(sectionID == currentSectionID)
into my editor and run static analysis, I get: "JAVA0078 Floating point values compared with =="
What is wrong with using == to compare floating point values? What is the correct way to do it?
the correct way to test floats for 'equality' is:
if(Math.abs(sectionID - currentSectionID) < epsilon)
where epsilon is a very small number like 0.00000001, depending on the desired precision.
Floating point values can be off by a little bit, so they may not report as exactly equal. For example, setting a float to "6.1" and then printing it out again, you may get a reported value of something like "6.099999904632568359375". This is fundamental to the way floats work; therefore, you don't want to compare them using equality, but rather comparison within a range, that is, if the diff of the float to the number you want to compare it to is less than a certain absolute value.
This article on the Register gives a good overview of why this is the case; useful and interesting reading.
Just to give the reason behind what everyone else is saying.
The binary representation of a float is kind of annoying.
In binary, most programmers know the correlation between 1b=1d, 10b=2d, 100b=4d, 1000b=8d
Well it works the other way too.
.1b=.5d, .01b=.25d, .001b=.125, ...
The problem is that there is no exact way to represent most decimal numbers like .1, .2, .3, etc. All you can do is approximate in binary. The system does a little fudge-rounding when the numbers print so that it displays .1 instead of .10000000000001 or .999999999999 (which are probably just as close to the stored representation as .1 is)
Edit from comment: The reason this is a problem is our expectations. We fully expect 2/3 to be fudged at some point when we convert it to decimal, either .7 or .67 or .666667.. But we don't automatically expect .1 to be rounded in the same way as 2/3--and that's exactly what's happening.
By the way, if you are curious the number it stores internally is a pure binary representation using a binary "Scientific Notation". So if you told it to store the decimal number 10.75d, it would store 1010b for the 10, and .11b for the decimal. So it would store .101011 then it saves a few bits at the end to say: Move the decimal point four places right.
(Although technically it's no longer a decimal point, it's now a binary point, but that terminology wouldn't have made things more understandable for most people who would find this answer of any use.)
What is wrong with using == to compare floating point values?
Because it's not true that 0.1 + 0.2 == 0.3
As of today, the quick & easy way to do it is:
if (Float.compare(sectionID, currentSectionID) == 0) {...}
However, the docs do not clearly specify the value of the margin difference (an epsilon from #Victor 's answer) that is always present in calculations on floats, but it should be something reasonable as it is a part of the standard language library.
Yet if a higher or customized precision is needed, then
float epsilon = Float.MIN_NORMAL;
if(Math.abs(sectionID - currentSectionID) < epsilon){...}
is another solution option.
I think there is a lot of confusion around floats (and doubles), it is good to clear it up.
There is nothing inherently wrong in using floats as IDs in standard-compliant JVM [*]. If you simply set the float ID to x, do nothing with it (i.e. no arithmetics) and later test for y == x, you'll be fine. Also there is nothing wrong in using them as keys in a HashMap. What you cannot do is assume equalities like x == (x - y) + y, etc. This being said, people usually use integer types as IDs, and you can observe that most people here are put off by this code, so for practical reasons, it is better to adhere to conventions. Note that there are as many different double values as there are long values, so you gain nothing by using double. Also, generating "next available ID" can be tricky with doubles and requires some knowledge of the floating-point arithmetic. Not worth the trouble.
On the other hand, relying on numerical equality of the results of two mathematically equivalent computations is risky. This is because of the rounding errors and loss of precision when converting from decimal to binary representation. This has been discussed to death on SO.
[*] When I said "standard-compliant JVM" I wanted to exclude certain brain-damaged JVM implementations. See this.
Foating point values are not reliable, due to roundoff error.
As such they should probably not be used for as key values, such as sectionID. Use integers instead, or long if int doesn't contain enough possible values.
This is a problem not specific to java. Using == to compare two floats/doubles/any decimal type number can potentially cause problems because of the way they are stored.
A single-precision float (as per IEEE standard 754) has 32 bits, distributed as follows:
1 bit - Sign (0 = positive, 1 = negative)
8 bits - Exponent (a special (bias-127) representation of the x in 2^x)
23 bits - Mantisa. The actuall number that is stored.
The mantisa is what causes the problem. It's kinda like scientific notation, only the number in base 2 (binary) looks like 1.110011 x 2^5 or something similar.
But in binary, the first 1 is always a 1 (except for the representation of 0)
Therefore, to save a bit of memory space (pun intended), IEEE deccided that the 1 should be assumed. For example, a mantisa of 1011 really is 1.1011.
This can cause some issues with comparison, esspecially with 0 since 0 cannot possibly be represented exactly in a float.
This is the main reason the == is discouraged, in addition to the floating point math issues described by other answers.
Java has a unique problem in that the language is universal across many different platforms, each of which could have it's own unique float format. That makes it even more important to avoid ==.
The proper way to compare two floats (not-language specific mind you) for equality is as follows:
if(ABS(float1 - float2) < ACCEPTABLE_ERROR)
//they are approximately equal
where ACCEPTABLE_ERROR is #defined or some other constant equal to 0.000000001 or whatever precision is required, as Victor mentioned already.
Some languages have this functionality or this constant built in, but generally this is a good habit to be in.
Here is a very long (but hopefully useful) discussion about this and many other floating point issues you may encounter: What Every Computer Scientist Should Know About Floating-Point Arithmetic
In addition to previous answers, you should be aware that there are strange behaviours associated with -0.0f and +0.0f (they are == but not equals) and Float.NaN (it is equals but not ==) (hope I've got that right - argh, don't do it!).
Edit: Let's check!
import static java.lang.Float.NaN;
public class Fl {
public static void main(String[] args) {
System.err.println( -0.0f == 0.0f); // true
System.err.println(new Float(-0.0f).equals(new Float(0.0f))); // false
System.err.println( NaN == NaN); // false
System.err.println(new Float( NaN).equals(new Float( NaN))); // true
}
}
Welcome to IEEE/754.
First of all, are they float or Float? If one of them is a Float, you should use the equals() method. Also, probably best to use the static Float.compare method.
You can use Float.floatToIntBits().
Float.floatToIntBits(sectionID) == Float.floatToIntBits(currentSectionID)
The following automatically uses the best precision:
/**
* Compare to floats for (almost) equality. Will check whether they are
* at most 5 ULP apart.
*/
public static boolean isFloatingEqual(float v1, float v2) {
if (v1 == v2)
return true;
float absoluteDifference = Math.abs(v1 - v2);
float maxUlp = Math.max(Math.ulp(v1), Math.ulp(v2));
return absoluteDifference < 5 * maxUlp;
}
Of course, you might choose more or less than 5 ULPs (‘unit in the last place’).
If you’re into the Apache Commons library, the Precision class has compareTo() and equals() with both epsilon and ULP.
you may want it to be ==, but 123.4444444444443 != 123.4444444444442
If you *have to* use floats, strictfp keyword may be useful.
http://en.wikipedia.org/wiki/strictfp
Two different calculations which produce equal real numbers do not necessarily produce equal floating point numbers. People who use == to compare the results of calculations usually end up being surprised by this, so the warning helps flag what might otherwise be a subtle and difficult to reproduce bug.
Are you dealing with outsourced code that would use floats for things named sectionID and currentSectionID? Just curious.
#Bill K: "The binary representation of a float is kind of annoying." How so? How would you do it better? There are certain numbers that cannot be represented in any base properly, because they never end. Pi is a good example. You can only approximate it. If you have a better solution, contact Intel.
As mentioned in other answers, doubles can have small deviations. And you could write your own method to compare them using an "acceptable" deviation. However ...
There is an apache class for comparing doubles: org.apache.commons.math3.util.Precision
It contains some interesting constants: SAFE_MIN and EPSILON, which are the maximum possible deviations of simple arithmetic operations.
It also provides the necessary methods to compare, equal or round doubles. (using ulps or absolute deviation)
In one line answer I can say, you should use:
Float.floatToIntBits(sectionID) == Float.floatToIntBits(currentSectionID)
To make you learned more about using related operators correctly, I am elaborating some cases here:
Generally, there are three ways to test strings in Java. You can use ==, .equals (), or Objects.equals ().
How are they different? == tests for the reference quality in strings meaning finding out whether the two objects are the same. On the other hand, .equals () tests whether the two strings are of equal value logically. Finally, Objects.equals () tests for any nulls in the two strings then determine whether to call .equals ().
Ideal operator to use
Well this has been subject to lots of debates because each of the three operators have their unique set of strengths and weaknesses. Example, == is often a preferred option when comparing object reference, but there are cases where it may seem to compare string values as well.
However, what you get is a falls value because Java creates an illusion that you are comparing values but in the real sense you are not. Consider the two cases below:
Case 1:
String a="Test";
String b="Test";
if(a==b) ===> true
Case 2:
String nullString1 = null;
String nullString2 = null;
//evaluates to true
nullString1 == nullString2;
//throws an exception
nullString1.equals(nullString2);
So, it’s way better to use each operator when testing the specific attribute it’s designed for. But in almost all cases, Objects.equals () is a more universal operator thus experience web developers opt for it.
Here you can get more details: http://fluentthemes.com/use-compare-strings-java/
The correct way would be
java.lang.Float.compare(float1, float2)
One way to reduce rounding error is to use double rather than float. This won't make the problem go away, but it does reduce the amount of error in your program and float is almost never the best choice. IMHO.