Strange float division result - java

I have occurred in this strange division error in a grails project (But I think grails has little to do with it, is a groovy or java question I think):
If in the groovy console I run this
float money = -1.30
float r = 0.01
println ((money/r).class.name)
println ((money/r).floatValue())
println ((money/r).toString() )
I get this output
java.lang.Double
-130.0
-129.99999813735482
The float division in groovy give me a Double, and this is correct but
why the Double toString() give me a so strange value "-129.99999813735482" and
not the correct "-130.0"?

From the Floating-Point Guide:
Why don’t my numbers, like 0.1 + 0.2 add up to a nice round 0.3, and
instead I get a weird result like 0.30000000000000004?
Because internally, computers use a format (binary floating-point)
that cannot accurately represent a number like 0.1, 0.2 or 0.3 at all.
When the code is compiled or interpreted, your “0.1” is already
rounded to the nearest number in that format, which results in a small
rounding error even before the calculation happens.
Specifically, neither 1.3 nor 0.01 can be accurately represented by a float.

As everyone says, double and float aren't precise enough for what you're trying to do.
One solution is to not use float as your object type, and do:
def money = -1.30
def r = 0.01
println ((money/r).class.name)
println ((money/r).floatValue())
println ((money/r).toString() )
As you can see, Groovy uses BigDecimal, which means the output is:
java.math.BigDecimal
-130.0
-130

By doing the floatValue, you are limiting the precission of the value. So the JVM does a rounding and you get the different value.
And before you say "but 130.0 should be the value calculated because it is the correct one" keep in mind that the computer uses binary format to represent decimal numbers and this causes rounding errors with fractions (try to represent 0.3 in binary to understand why).

Related

Java double precision sum trouble

I would like to know why I get this error. (this is Display log of Eclipse debug)
var
(double) 2.8
tot.getIva()
(java.lang.Double) 0.17
var+tot.get()
(double) 2.9699999999999998
I can not understand why I did not get simply 2.97!
If you wanted 2.97, you should have used BigDecimal.
doubles are stored as fractions in binary, not decimal. So 3.75, for example, is just stored as 2^1 + 2^0 + 2^(-1) + 2^(-2).
2.8 and 0.17 cannot be represented exactly as binary fractions, so there's going to be some rounding error.
You may also find this article helpful.
This is due to the precision of floating point types in java (float and double). If you need indefinite precision you should try using BigDecimal instead of double.

Weird Java fraction behavior

I have seen a very weird behaviour in Java's double variable, as I'm trying to simply add small fractions to a double and I see a completely bizarre results.
double test = 0;
test += 0.71;
test += 0.2;
Now I'd expect the result to be:
test = 0.91
Right? Wrong!
In reality, this is the number I get in my test double:
test = 0.9099999999999999
Now while this is very close, it's a very bizarre fraction loss, and in the long run it causes serious bugs in my program.
With a float I've gotten even a weirder result.
Any help would be greatly appreciated.
Thanks
There is nothing bizarre about it at all. 0.91, 0.71 and 0.2 are not representable as a IEEE754 floating point values as they would have a recurring fractional part when represented in binary. The situation is entirely analogous to trying to represent 1/3 in base 10 with a finite number of digits. You can't do it.
What you are seeing is a rounding error that is normal when doing floating point calculations. You have to code around it. So for instance, you can't reliably compare for equality, you have to see the two numbers are within some small delta of each other. For a slightly more in depth but still understandable explanation see The Floating Point Guide.
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
double can only approximate most fractional values. This means you need to use some rounding if you want to get your expect result. Or you can use BigDecimal which takes care of this issue for you.
double test = 0;
test += 0.71;
test += 0.2;
System.out.printf("%.2f%n", test);
prints
0.91
For your own interest
System.out.println("0.71 is actually " + new BigDecimal(0.71));
System.out.println("0.2 is actually " + new BigDecimal(0.2));
System.out.println("0.71+0.2 is actually " + new BigDecimal(0.71 + 0.2));
System.out.println("0.91 is actually " + new BigDecimal(0.91));
System.out.println("0.71+0.2 == 0.91 is " + (0.71 + 0.2 == 0.91));
prints
0.71 is actually 0.70999999999999996447286321199499070644378662109375
0.2 is actually 0.200000000000000011102230246251565404236316680908203125
0.71+0.2 is actually 0.9099999999999999200639422269887290894985198974609375
0.91 is actually 0.91000000000000003108624468950438313186168670654296875
0.71+0.2 == 0.91 is false
Java uses something called floating-point to represent decimals. They use exponential notation. Here's what I mean:
There is a multiplier (M), and an exponent between 1023 and -1022 (E).
A number (N) is represented like this: M * 2^E.
4.25 is represented like this:
17 * 2^-2.
0.91 cannot be represented in base 2 exactly, but Java can get pretty close:
0.909999999999..
Therefore, it is impossible to accurately add these numbers together.

Java BigDecimal precision problems

I know the following behavior is an old problem, but still I don't understand.
System.out.println(0.1 + 0.1 + 0.1);
Or even though I use BigDecimal
System.out.println(new BigDecimal(0.1).doubleValue()
+ new BigDecimal(0.1).doubleValue()
+ new BigDecimal(0.1).doubleValue());
Why this result is: 0.30000000000000004 instead of: 0.3?
How can I solve this?
What you actually want is
new BigDecimal("0.1")
.add(new BigDecimal("0.1"))
.add(new BigDecimal("0.1"));
The new BigDecimal(double) constructor gets all the imprecision of the double, so by the time you've said 0.1, you've already introduced the rounding error. Using the String constructor avoids the rounding error associated with going via the double.
First never, never use the double constructor of BigDecimal. It may be the right thing in a few situations but mostly it isn't
If you can control your input use the BigDecimal String constructor as was already proposed. That way you get exactly what you want. If you already have a double (can happen after all), don't use the double constructor but instead the static valueOf method. That has the nice advantage that we get the cannonical representation of the double which mitigates the problem at least.. and the result is usually much more intuitive.
This is not a problem of Java, but rather a problem of computers generally. The core problem lies in the conversion from decimal format (human format) to binary format (computer format). Some numbers in decimal format are not representable in binary format without infinite repeating decimals.
For example, 0.3 decimal is 0.01001100... binary But a computer has a limited "slots" (bits) to save a number, so it cannot save all the whole infinite representation. It saves only
0.01001100110011001100 (for example). But that number in decimal is no longer 0.3, but 0.30000000000000004 instead.
Try this:
BigDecimal sum = new BigDecimal(0.1).add(new BigDecimal(0.1)).add(new BigDecimal(0.1));
EDIT: Actually, looking over the Javadoc, this will have the same problem as the original. The constructor BigDecimal(double) will make a BigDecimal corresponding to the exact floating-point representation of 0.1, which is not exactly equal to 0.1.
This, however, gives the exact result, since integers CAN always be expressed exactly in floating-point representation:
BigDecimal one = new BigDecimal(1);
BigDecimal oneTenth = one.divide(new BigDecimal(10));
BigDecimal sum = oneTenth.add(oneTenth).add(oneTenth);
The problem you have is that 0.1 is represented with a slightly higher number e.g.
System.out.println(new BigDecimal(0.1));
prints
0.1000000000000000055511151231257827021181583404541015625
The Double.toString() takes into account this representation error so you don't see it.
Similarly 0.3 is represented by a value slightly lower than it really is.
0.299999999999999988897769753748434595763683319091796875
If you multiply the represented value of 0.1 by 3 you don't get the represented value for 0.3, you instead get something a little higher
0.3000000000000000166533453693773481063544750213623046875
This is not just a representation error but also a rounding error caused by the operations. This is more than the Double.toString() will correct and so you see the rounding error.
The moral of the story, if you use float or double also round the solution appropriately.
double d = 0.1 + 0.1 + 0.1;
System.out.println(d);
double d2 = (long)(d * 1e6 + 0.5) / 1e6; // round to 6 decimal places.
System.out.println(d2);
prints
0.30000000000000004
0.3

Java division for double and float without E

I'm doing some large number divisions (long/long to double, and int/int to float).. But I bump, to a problem when the results include the "E". I know we can use NumberFormat to format when displaying, but that's not what I. Just want the result of the divisions to not involve the "E", i.e. just round it up to the closest float/double that fits in the space.
Anybody got an idea?
The internal representation of floating point number does not have a switch for E presence or not (check IEEE-754). So your float/double number is just number (not a number with E or without it).
The only place where you get E is when you print this value out. And while Java uses number formater for printing, so I don't see a point why you don't want to use it here.
System.out.println(new DecimalFormat("#.#####").format(doubleValue));
The general problem that double and float in binary format. It not always possible to convert decimal fraction to binary fraction. For example 0.2 decmal fraction have infinitely many digits in binary (double) format. So whe converted from bynary format to decimal string, it result something like "0.2000000001" what displayed with E. To solve this problem you can use BigDecimal class what contains number in decimal format, so no E problem - it can easy rounded to any decimal point by setScale method. Or you can sore double as is, an write it to output by String.format("My value are: %.3f", value) - i recommend this way.
If you just want round you value to decimal point you can use:
new BigDecimal(val).setScale(3, RoundingMode.HALF_EVEN).doubleValue()
But there no any garanty what this core return double with fine fraction numbers.

Java Glitch? Subtracting numbers?

Is this a glitch in Java?
I go to solve this expression: 3.1 - 7.1
I get the answer: -3.9999999999999996
What is going on here?
A great explanation can be found here. http://www.ibm.com/developerworks/java/library/j-jtp0114/
Floating point arithmetic is rarely exact. While some numbers, such
as 0.5, can be exactly represented as a binary (base 2) decimal (since
0.5 equals 2-1), other numbers, such as 0.1, cannot be. As a result, floating point operations may result in rounding errors, yielding a
result that is close to -- but not equal to -- the result you might
expect. For example, the simple calculation below results in
2.600000000000001, rather than 2.6:
double s=0;
for (int i=0; i<26; i++)
s += 0.1;
System.out.println(s);
Similarly, multiplying .1*26 yields a result different from that of
adding .1 to itself 26 times. Rounding errors become even more serious
when casting from floating point to integer, because casting to an
integral type discards the non-integral portion, even for calculations
that "look like" they should have integral values. For example, the
following statements:
double d = 29.0 * 0.01;
System.out.println(d);
System.out.println((int) (d * 100));
will produce as output:
0.29
28
which is probably not what you might expect at first.
See the provided reference for more information.
As mentioned by several others you cannot count on double if you would like to get an exact decimal value, e.g. when implementing monetary applications. What you should do instead is to take a closer look at BigDecimal:
BigDecimal a = new BigDecimal("3.1");
BigDecimal b = new BigDecimal("7.1");
BigDecimal result = a.subtract(b);
System.out.println(result); // Prints -4.0
Computers are 100% so in the math world that is correct, to the average person it is not. Java cant have a error on a specific number as it is just code that runs the same way but has a different input!
P.S. Google how to round a number
rounding errors in floating points
same way that 3 * 0.1 != 0.3 (when it's not folded by the compiler at least)
Automatic type promotion is happening and that is the result.
Here is some resource to learn.
http://docs.oracle.com/javase/specs/jls/se5.0/html/conversions.html
The next step would be is to learn to use formatters to format it to the given precision / requirements.

Categories

Resources