Strange floating-point behaviour in a Java program
The most common storage for floating-point values in programming languages - IEEE singles and doubles - does not have exact representations for most decimal fractions.
The reason is that they store values in binary floating-point format, rather than decimal floating-point format. The only fractional values which can be represented exactly are those which are sums of negative powers of two. Numbers like:
- 0.5 (2^-1)
- 0.125 (2^-3)
- 0.625 (2^-1 + 2^-3)
Etc.
What you are seeing is the fact that representations of numbers like 0.96 are not exactly representable, because they are not expressible as a sum of negative powers of two. Thus, when printed out with full precision as a decimal fraction, they won't match the original value.
Strange behaviour managing double and float values (Java)
Your problem is due to the way in which double
and float
work.
In floating-point arithmetic, the computer only calculates to a certain precision, i.e. after so many decimal places, it just rounds the number off. 0.1
may seem like a nice round number in decimal, but in binary it is recurring - 0.0001100110011
and so on. With each calculation, the rounding-off makes the result a bit more inaccurate. Have a look at this page for a more thorough explanation.
double
is more precise than float
, but it will still display rounding errors like this.
To circumvent the problem, you could do one of two things. First, as Jon Skeet said in the comments, you could use arbitrary-precision arithmetic like BigDecimal
.
Alternatively, you could print out only a couple of decimal places like this:
String answer = String.format("%.2f", myNumber);
This will round off the printed value to 2 decimal places.
Hope this helps!
Strange behavior of float point
The primary cause of the difference is that .9 + x •.1 is larger than .1 + x • .9 for any x < 1, and Random.nextFloat
returns values less than 1. For the largest value it returns, the former expression is so close to 1 that rounding it to float
produces 1, but the latter expression is farther from 1, and rounding it to float
produces the next float
below 1.
The largest value that Random.nextFloat
returns is Random.nextfloat
16777215/16777216. Let M be this maximum, and let d = 1 − M = 1/16777216.
If we calculated t1
using real numbers, its maximum value would be .9 + M•.1. = .9 + (1−d)•.1 = 1−.1•d. Similarly, t2
would be .1 + M•.9 = .1 + (1-d)•.9 = 1-.9•d.
Now we can easily see that the maximum of t1
(if calculated with real numbers) is .1•d away from 1, but t2
is .9•d away from 1.
A reason the largest value Random.nextFloat
returns is 16777215/16777216 is that it is the largest float
less than 1. The precision of the float
format is such that the steps between representable values leading up to 1 are 1/16777216. This means that the two representable values in this neighborhood are 16777215/16777216 and 1, and d is the distance between them. Our calculations above show us the maximum value of t1
is just .1•d away from 1. So, when it is rounded to float
, 1 is the nearest float
.
In contrast, the maximum value of t2
is .9•d away from 1. This means it is just .1•d away from 16777215/16777216. So, when it is rounded to float
, 16777215/16777216 is the nearest float
.
That is using real-number arithmetic. Below, I will show the floating-point arithmetic. It has rounding errors, but these turn out to be small enough not to change the results.
In Java
, the source texts .1f
and .9f
are converted to float
, which yield 0.100000001490116119384765625 and 0.89999997615814208984375. The arithmetic in the expressions is performed using double
, and then the result is rounded to float
for assignment to t1
or t2
.
The largest that t1
can be may be calculated:
- Substituting the largest return value of
nextFloat
yields0.9f + 16777215./16777216 * 0.1f;
. - Evaluating in
double
arithmetic yields 0.999999971687793642871611154987476766109466552734375. - Converting to
float
yields 1, because thatdouble
value is between 0.999999940395355224609375 (the next lowerfloat
value) and 1 (the next higher), and it is closer to the latter than to the former.
The largest that t2
can be may be calculated:
- Substituting the largest return value of
nextFloat
yields0.1f + 16777215./16777216 * 0.9f;
. - Evaluating in
double
arithmetic yields 0.99999992400407933246242464520037174224853515625. - Converting to
float
yields 0.999999940395355224609375.
Floating point Weird Phenomenon in Java
This is a typical floating point result runded.
You get different results from Float
and Double
:
System.out.println(4.0f-3.1f);
System.out.println(4.0d-3.1d);
Output:
0.9000001
0.8999999999999999
This is because 0.1
cannot be represented evenly in base 2, and cause a loss of precision. For example :
System.out.println(2.0f-1.9f);
System.out.println(2.0d-1.9d);
Should both return 0.1
but in fact will output :
0.100000024
0.10000000000000009
Java strange behaviour when using double division in declaration/init
Because double b = 1 / 2;
first computes 1 / 2
, which is simple integer division and evaluates to 0, and then casts that value to the double 0.0
to assign it to b
.
How come Java handle floating-point-imprecision so strange?
Java’s default formatting produces just enough decimal digits to uniquely distinguish the floating-point number from neighboring floating-point numbers.
When the source text 0.3
is converted to double
, the result is the closest double
value, which is 0.299999999999999988897769753748434595763683319091796875. When this is printed, “0.3” suffices to uniquely identify it, because 0.299999999999999988897769753748434595763683319091796875 is of course the closest value, since that is how we got it from 0.3
in the first place.
The source texts 0.1
and 0.2
produce 0.1000000000000000055511151231257827021181583404541015625 and
0.200000000000000011102230246251565404236316680908203125. When these are added in double
format, the result is 0.3000000000000000444089209850062616169452667236328125. Note this is different from the number above. So, when it is printed, “0.3” does not suffice to distinguish it from the neighboring value 0.299999999999999988897769753748434595763683319091796875. It is necessary to produce “0.30000000000000004” to show that it is different.
When the value is converted to float
, formatting for float
is used, instead of formatting for double
. When formatting for float
, only values representable in float
are candidates for what are neighbors. The value representable in float
that is nearest 0.3 is 0.300000011920928955078125. This is also the result of (float) 0.1 + (float) 0.2
, and it is sufficiently distinguished from the neighboring float
values (0.2999999821186065673828125 and 0.3000000417232513427734375) by printing merely “0.3”.
Weird Java fraction behavior
That's the magic of binary encoding of floating point values (look for IEEE754 : http://en.wikipedia.org/wiki/IEEE_754-2008 ). If you want to be sure to never have this kind of things, you're maybe looking for BigDecimal :
http://docs.oracle.com/javase/1.5.0/docs/api/java/math/BigDecimal.html
Basic rules :
- don't use equality tests when dealing with floating point numbers (you must test gaps)
- round numbers you're displaying (usually using DecimalFormat)
- don't use floating point numbers for financial applications
- the float is generally the way to go for scientific or industrial operations, as long as you understand IEEE754
Java: Inaccuracy using double
Some decimals cannot be exactly represented by double values. 0.3 is one of those values.
All integer values less than a certain number (I forget which) happen to have an exact representation by a double value, so you don't see the approximation.
Consider how we think of numbers: the number 123 is represented as (1 * 100) + (2 * 10) + (3 * 1). We use 10 as our base. Binary numbers use two. So when you look at fractions of a number, how could you represent 0.3 by adding individual powers of 2? You can't. The best you can come up with is about 0.30000000000000004 (I'd have to see the exact binary digits to see how it reaches that).
Related Topics
How to Get Screen Resolution in Java
Convert Latitude/Longitude Point to a Pixels (X,Y) on Mercator Projection
Border with Rounded Corners & Transparency
How Good Is Java's Uuid.Randomuuid
Simpledateformat Parsing Date with 'Z' Literal
How to Split a Comma-Separated String
Is It Good Practice to Use Java.Lang.String.Intern()
Embed Java into a C++ Application
Swagger Ui Is Not Loading Swagger API Json
What's the Difference Between Primitive and Reference Types
Why Is "Extends T" Allowed But Not "Implements T"
How Is an Overloaded Method Chosen When a Parameter Is the Literal Null Value
Error: Selection Does Not Contain a Main Type
Java String Remove All Non Numeric Characters But Keep the Decimal Separator