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.
Related
Why this assertion fails in Java:
double eps = 0.00000000000001;
double ten = 10.0;
double result = (ten - (ten - eps));
Assert.assertTrue(result <= eps);
If I remove one zero before digit 1 in eps, the assertion passes. I assume that this is related to the floating point implementation, but I'm not sure exactly how.
Also, if I replace digit 1 with 2 (like 0.00000000000002) the assertion passes as well. In that case, I can even add more zeros before the digit 2, the test will still pass. I tried with Double.MIN_VALUE (4.9E-324) and the assertion also passed.
Can someone, please, explain in more details:
Why the assertion passes with eps = 1.0E-13 but not with eps = 1.0E-14
Why the assertion passes with eps = Double.MIN_VALUE (4.9E-324) and not with eps = 1.0E-14
EDIT: The assertion also fails when I increase the eps to 1.0E-8: double eps = 0.00000001;
This is because of the organization of the bytes that represents the double type.
As you can see on the image below, it is a 64 bit structure. The bits [b0 .. b51] are 'concatenated' and elevated by the exponent, [b52 .. b62].
And the equation that determines what each combination of bits represents in real value, is:
With this formula, you have that the minimum value is represented by
3ff0 0000 0000 000116 => 1.0000000000000002
For better explanation, see this wiki page Double-precision floating-point format
In the last assertion you're comparing result (1.0658141036401503E-14) and eps (1.0E-14), matimatically that shoud be wrong as espected from the assertion, result in this case is bigger than eps. If you remove one 0 from eps rps become 1.0E-13 that is bigger than 1.0658141036401503E-14 in this case
The problem is that the assertion code is wrong-ish in a sense that it does not take into account the second subtraction ten - (ten - eps).
Let's explain this step by step. Let eps = 0.00000001 (1.0E-8). In this case, 10.0 - eps is 9.99999999. So far, so good. However, 10.0 - 9.99999999 is 0.00000001000000082740371, which is around the expected result of 0.00000001, but just a little bit larger, because floating point arithmetic (usually) gives just good enough approximation. Therefore, for some eps values the final result is very close, but just below the actual result and for some values it is again very close, but just above the actual result.
The code needs to be fixed in order to take into account that the result of the second subtraction is also just an approximation.
One way to do it is to change the assertion to:
Assert.assertTrue(Math.abs(result - eps) <= eps);
In order to understand more on floating point arithmetics, I've found this article quite well written: http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html
This quote summarize why the errors in floating point arithmetics happen:
There are two reasons why a real number might not be exactly
representable as a floating-point number. The most common situation is
illustrated by the decimal number 0.1. Although it has a finite
decimal representation, in binary it has an infinite repeating
representation. Thus when β = 2, the number 0.1 lies strictly between
two floating-point numbers and is exactly representable by neither of
them.
Try following code:
BigDecimal eps1 = new BigDecimal(eps);
BigDecimal ten1 = new BigDecimal(ten);
BigDecimal result1 = ten1.subtract( ten1.subtract(eps1) );
It should be stable regardless eps
Why does the following code:
System.out.println((int)(19.99 * 100));
produce the result "1998"?
Rounding errors. If you look at the result of your calculation without the cast, you get:
1998.9999999999998
So when you cast to int, the decimal part is dropped, not rounded up, and you get 1998.
The moral is, if you need an exact answer, don't use float / double at all. If you're talking about a discrete value like money, use int and deal with the atomic unit (eg. pence.) If you do need exact decimals, then BigDecimal is your friend.
While you can bodge the result here using Math.round() to bring the result to where it's expected, this won't work in all cases and fails to address the underlying issue.
That is because 19.99 cannot be represented exactly.
System.out.println(new BigDecimal(19.99));
prints the value this actually represents which is the closest to 19.99 it can represent.
19.989999999999998436805981327779591083526611328125
and 19.99 * 100 is
System.out.println(new BigDecimal(19.99 * 100));
which is
1998.999999999999772626324556767940521240234375
The problem is that you have a representation error in 19.99 which is still there when multiplied by 100 you get a number which is slightly too small.
if you multiply by 100 and round down which is what (int) does you should expect to get 1998.
An alternative is
System.out.println(Math.round(19.99 * 100));
because the calculation of 19.99 * 100 will result in 1998.999999 and you are casting it to int it will discard the fractional part of it.
It is due to a rounding issues. double and float are prone to these issues, which is why it is recommended you use the BigDecimal class.
This code should print what is expected:
BigDecimal bg = new BigDecimal("19.99");
System.out.println(bg.multiply(new BigDecimal("10")));
This yields:
199.90
System.out.println((19.99 * 100));
produces the result 1998.9999999999998 by adding int casting it truncates the fraction part and returns 1998
Floating point data types (float and double in Java) can only approximately represent most decimal values. See Joshua Bloch's words of wisdom on the subject for more details.
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.
In my JAVA program there is code like this:
int f_part = (int) ((f_num - num) * 100);
f_num is double and num is long. I just want to take the fractional part out and assign it to f_part. But some times f_part value is one less than it's value. Which means if f_num = 123.55 and num = 123, But f_part equals to 54. And it happens only f_num and num is greater than 100. I don't know why this happening. Please can someone explain why this happens and way to correct it.
This is due to the limited precision in doubles.
The root of your problem is that the literal 123.55 actually represents the value 123.54999....
It may seem like it holds the value 123.55 if you print it:
System.out.println(123.55); // prints 123.55
but in fact, the printed value is an approximation. This can be revealed by creating a BigDecimal out of it, (which provides arbitrary precision) and print the BigDecimal:
System.out.println(new BigDecimal(123.55)); // prints 123.54999999999999715...
You can solve it by going via Math.round but you would have to know how many decimals the source double actually entails, or you could choose to go through the string representation of the double in fact goes through a fairly intricate algorithm.
If you're working with currencies, I strongly suggest you either
Let prices etc be represented by BigDecimal which allows you to store numbers as 0.1 accurately, or
Let an int store the number of cents (as opposed to having a double store the number of dollars).
Both ways are perfectly acceptable and used in practice.
From The Floating-Point Guide:
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.
It looks like you're calculating money values. double is a completely inappropriate format for this. Use BigDecimal instead.
int f_part = (int) Math.round(((f_num - num) * 100));
This is one of the most often asked (and answered) questions. Floating point arithmetics can not produce exact results, because it's impossible to have an inifinity of real numbers inside 64 bits. Use BigDecimal if you need arbitrary precision.
Floating point arithmetic is not as simple as it may seem and there can be precision issues.
See Why can't decimal numbers be represented exactly in binary?, What Every Computer Scientist Should Know About Floating-Point Arithmetic for details.
If you need absolutely sure precision, you might want to use BigDecimal.
I happened upon these values in my ColdFusion code but the Google calculator seems to have the same "bug" where the difference is non-zero.
416582.2850 - 411476.8100 - 5105.475 = -2.36468622461E-011
http://www.google.com/search?hl=en&rlz=1C1GGLS_enUS340US340&q=416582.2850+-+411476.8100+-+5105.475&aq=f&oq=&aqi=
JavaCast'ing these to long/float/double doesn't help- it results in other non-zero differences.
This is because decimal numbers that "look" round in base 10, are not exactly representable in base 2 (which is what computers use to represent floating point numbers). Please see the article What Every Computer Scientist Should Know About Floating-Point Arithmetic for a detailed explanation of this problem and workarounds.
Floating-point inaccuracies (there are an infinite number of real numbers and only a finite number of 32- or 64-bit numbers to represent them with).
If you can't handle tiny errors, you should use BigDecimal instead.
Use PrecisionEvaluate() in ColdFusion (it'll use BigDecimal in Java)
zero = PrecisionEvaluate(416582.2850 - 411476.8100 - 5105.475);
unlike Evaulate(), no "" is needed.
Since computer stores numbers in binary, float numbers are imprecise. 1E-11 is a tiny difference due to rounding these decimal numbers to the nearest representable binary number.
This "bug" is not a bug. It's how floating point arithmetic works. See: http://docs.sun.com/source/806-3568/ncg_goldberg.html
If you want arbitrary precision in Java, use BigDecimal:
BigDecimal a = new BigDecimal("416582.2850");
BigDecimal b = new BigDecimal("411476.8100");
BigDecimal c = new BigDecimal("5105.475");
System.out.println(a.subtract(b).subtract(c)); // 0.0
The problem is the inexact representation of floating point types. Because these can't be exactly represented as floats, you get some precision loss that results in operations have small errors. Typically with floats you want to compare whether the result is equal to another value within some small epislon (error factor).
These are floating point issues and using BigDecimal will fix it.
Changing the order of subtraction also yields zero in Google.
416582.2850 - 5105.475 - 411476.8100 = 0