Why do floating-point numbers have roundoff errors? [duplicate] - java

This question already has answers here:
Is floating point math broken?
(31 answers)
Closed 8 years ago.
I have been struggling to understand the concept of roundoff errors in Java with floating-point. While I understand that double is not supposed to be used for financial calculations, I don't understand why the 'd' variable does not come out to 0.0. How can I get this to print out the first println?
package zetcom;
public class floatingComparison {
public static void main(String[] args) {
double r = Math.sqrt(2);
double d = r * r - 2;
if (d == 0)
{
System.out.println("sqrt(2) squared minus 2 is 0");
}
else
{
System.out.println("sqrt(2) squared minus 2 is " + d);
}
}
}
Any explanation would be appreciated.

Short answer:
The square root of 2 requires an infinite number of digits -- that is, infinite precision. doubles have 52 bits of precision. That's a lot, but it's far, far short of infinite.
If you tried to represent 1/3 with two digits (0.33), you wouldn't be surprised at rounding errors, right? You'd multiply it by 3 and be not-at-all-surprised to get an answer of 0.99 instead of 1.0. It's the same thing exactly.
Digging a bit further...
What's a bit more un-intuitive is that numbers that can be represented with a infinite number of digits in base 10 might not be able to be represented by a finite number of digits in base 2 (which is what doubles and floats use). For instance, 1/10 is 0.1 in base 10, but it's 0.0001100110011... in base 2. So it will also be rounded off when you store it as a double, for the same reason as above: storing 1/10 in a finite number of digits in binary is as impossible as storing 1/3 in a finite number of digits in decimal.
Digging in even more...
And finally, you can look at it the other way around, too. While 1/3 is impossible to write in decimal with finite precision, it's just 0.1 in base 3.

Floating point numbers in virtually all languages are always approximate (bar powers of 2), because they cannot be accurately represented in binary. This is due to how computers process information: in bits.
For instance, how do you represent .3 in binary? You're always going to get a round off error if you try to achieve maximum precision with floating point numbers due to them having to be represented in binary.

Related

Java Conversion into Double

class Rextester
{
public static void main(String args[])
{
double b = 1.13f * 100;
System.out.println(b);
}
}
In the Above code when f is not appended to 1.13 the output is 112.99999999999999 but when f is appended to 1.13 the value us 113. Why is this behaviour?
The f suffix is telling Java that the number is a single precision floating point number, instead of a double floating point number.
The problem with floating point numbers in general, is that certain numbers cannot be precisely represented. Each bit of the mantissa of the internal representation represents a fraction with a power of 2 in the denominator, so 1/2, 1/4, 1/8, 1/16 etc. Then the computer will select the closest number that represents the number you want.
What is happening in your case is that when you leave out the f, it uses the full double precision bits, and gets the closest number to it (112.9999999999). When you do the f your are telling the program to round it up to the closest single precision floating point, so the first 9 that doesn't fit gets rounded and propagates up to the value of 113.
It is a bit of a matter of coincidence for this specific number. Don't assume that using single precision floating point will always give you the expected result. Floating point arithmetic is always a bit messy in computing.
When you add f to the decimal it makes it a float constant which has only about 6 digits of precision. This makes representation error much more likely and much bigger.
When you drop the use of f, the decimal is a double which has half a trillion times the precision. This makes the representation error much smaller, and when printed as a double you are less likely to see it.
When you print a double, the libraries expects there to be some representation error and will show you the simplest/shortest number which has the same representation as the double. (This is actually an infinite number of numbers which map to the same representation)
However, this implicit rounding will only correct a very small amount and is unlikely to correct for the representation error of a float. Note: if you print using a float instead of a double it will perform greater rounding, hiding the error.

Why is float value "++" operation different for 3.14? [duplicate]

This question already has answers here:
Is floating point math broken?
(31 answers)
Closed 4 years ago.
Code
public class test{
public static void main(String[] args){
double first = 3.14 ;
first++;
System.out.println(first);
}
}
Result
ubuntu#john:~/Desktop$ javac test.java
ubuntu#john:~/Desktop$ java test
Output : 4.140000000000001
I am getting expected answer for almost every other case...
Eg : For 4.14 ,result is 5.14...
Why this case is special ?
There are lots of numbers that can be expressed exactly in decimal, but not exactly in binary. That is, they have a terminating decimal representation, but no terminating binary representation.
To understand this, consider the number 1/3. It doesn't have a terminating decimal representation - we can keep writing 0.3333333333333 for a while, but sooner or later, we have to stop, and we still haven't quite written 1/3.
The same thing happens when we try to write 2.14 in binary. It's 10.001000111... and a bunch more 0s and 1s that eventually start repeating, in the same way as 0.333333 repeats in decimal.
Now a double is just a binary number with 53 significant figures. So it can't store exactly 2.14, but it can get very close. Now see what happens when we start incrementing it.
2.14 = 10.001000111 .... (53 significant figures, 51 of them after the dot)
3.14 = 11.001000111 .... (53 significant figures, 51 of them after the dot)
4.14 = 100.001000111 ... (53 significant figures, 50 of them after the dot)
5.14 = 101.001000111 ... (53 significant figures, 50 of them after the dot)
So we didn't lose any accuracy when we went from 2.14 to 3.14, because the part after the dot didn't change. Likewise when we went from 4.14 to 5.14.
But when we went from 3.14 to 4.14, we lost accuracy, because we needed one extra digit before the dot, so we lost a digit after the dot.
Now Java has a complicated algorithm for figuring out how to display a floating point number. Basically, it picks the shortest decimal representation that's closer to the floating point number you're trying to represent, than to any other floating point number. That way, if you write double d = 2.14;, then you'll get a floating point number that's SO CLOSE to 2.14, that it will always show up as 2.14 when you print it out.
But as soon as you start messing with the digits after the dot, the complexity of Java's printing algorithm kicks in - and the number can end up printed differently from how you expect.
So this won't happen when you increment a double, but don't change the number of digits before the dot. It can only happen when you increment a double past a power of 2; because this changes the number of digits before the dot.
To illustrate this, I ran this code.
for(int i = 0; i < 1000000000; i++) {
if ( i + 1 + 0.14 != i + 0.14 + 1 ) {
System.out.println(i + 0.14 + 1);
}
}
and got this output.
4.140000000000001
1024.1399999999999
2048.1400000000003
4096.139999999999
1048576.1400000001
2097152.1399999997
4194304.140000001
Observe that all these discrepant values are just past a power of two.
A float increment does not increment with 1, but with something that is a tiny bit off.
So it will go fine for a while, but after some time the answer will not be correct.
This is because of round-off 'problems' with float increments/decrements.
It's bad style. ++ and -- are intended to set a value to its next or previous value, like the next or previous integer, the next or previous element in an array (for pointers) for example
'Next' and 'previous' values are not well-defined for floats.
see: Is floating point math broken?

Java 1029 / 9.8 = 104.999 [duplicate]

This question already has answers here:
Is floating point math broken?
(31 answers)
Closed 8 years ago.
double test = 1029 / 9.8; // = 104.99999...
int inttest1 = (int) test; // 104
int inttest2 = (int)Math.floor(test); // 104
double testtt = 9.8 * 105; // 1029.0
1029 / 9.8 equals 105
but Java returns 104.9999...
More serious problem is integer casing result is 104, not 105
Why this happens and how can I avoid this result?
There are an infinite number of numbers, even in the limited range represented by Java. That's because mathematically, if you give me any two distinct numbers, I can average them to get a number between them. No matter how close they are.
And there are only a limited number of bits available to represent those numbers.
Hence, something has to give. What gives is the precision of the numbers. Not all numbers can be represented exactly, so some (the vast majority actually) are approximations.
For example, 0.1 cannot be represented exactly with IEEE754 encoding, even with a billion bits available to you.
See this answer for more information on the inherent imprecision of limited-storage floating point numbers.
Casting to an int implicitly drops any decimal. No need to call Math.floor() (assuming positive numbers)
To avoid this behavior use BigDecimal;
http://docs.oracle.com/javase/7/docs/api/java/math/BigDecimal.html
Standard floating point variables are in binary floating point. Many decimal floating point values (which are the ones you type in your code) have no exact representation in binary floating point. So it isn't doing the calculation with the exact numbers you entered but with some values very close to it. You can use Math.round to round the result to the precision you need and most likely the small error will disappear.
If you really need exact decimal calculation use BigDecimal but note that it is much slower.

Loss of precision after subtracting double from double [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Retain precision with Doubles in java
Alright so I've got the following chunk of code:
int rotation = e.getWheelRotation();
if(rotation < 0)
zoom(zoom + rotation * -.05);
else if(zoom - .05 > 0)
zoom(zoom - rotation * .05);
System.out.println(zoom);
Now, the zoom variable is of type double, initially set to 1. So, I would expect the results to be like 1 - .05 = .95; .95 - .05 = .9; .9 - .05 = .85; etc. This appears to be not the case though when I print the result as you can see below:
0.95
0.8999999999999999
0.8499999999999999
0.7999999999999998
0.7499999999999998
0.6999999999999997
Hopefully someone is able to clearly explain. I searched the internet and I read it has something to do with some limitations when we're storing floats in binary but I still don't quite understand. A solution to my problem is not shockingly important but I would like to understand this kind of behavior.
Java uses IEEE-754 floating point numbers. They're not perfectly precise. The famous example is:
System.out.println(0.1d + 0.2d);
...which outputs 0.30000000000000004.
What you're seeing is just a symptom of that imprecision. You can improve the precision by using double rather than float.
If you're dealing with financial calculations, you might prefer BigDecimal to float or double.
float and double have limited precision because its fractional part is represented as a series of powers of 2 e.g. 1/2 + 1/4 + 1/8 ... If you have an number like 1/10 it has to be approximated.
For this reason, whenever you deal with floating point you must use reasonable rounding or you can see small errors.
e.g.
System.out.printf("%.2f%n", zoom);
To minimise round errors, you could count the number of rotations instead and divide this int value by 20.0. You won't see a rounding error this way, and it will be faster, with less magic numbers.
float and double have precision issues. I would recommend you take a look at the BigDecimal Class. That should take care of precision issues.
Since decimal numbers (and integer numbers as well) can have an infinite number of possible values, they are impossible to map precisely to bits using a standard format. Computers circumvent this problem by limiting the range the numbers can assume.
For example, an int in java can represent nothing larger then Integer.MAX_VALUE or 2^31 - 1.
For decimal numbers, there is also a problem with the numbers after the comma, which also might be infinite. This is solved by not allowing all decimal values, but limiting to a (smartly chosen) number of possibilities, based on powers of 2. This happens automatically but is often nothing to worry about, you can interpret your result of 0.899999 as 0.9. In case you do need explicit precision, you will have to resort to other data types, which might have other limitations.

Weird floor rounding [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Moving decimal places over in a double
I am having this weird problem in Java, I have following code:
double velocity = -0.07;
System.out.println("raw value " + velocity*200 );
System.out.println("floored value " + Math.floor(velocity*200) );
I have following output:
raw value -14.000000000000002
floored value -15.0
Those traling 0002 screw everything up, and BTW there should not be that traling 2, I think it should be all zeroes after decimal point, can I get rid of that 2?
Update: Thanks for help, Guys do you know any way to make floor rounding on BigDecimal object without calling doubleValue method?
Because floor(-14.000000000000002) is indeed -15!
You see, floor is defined as the maximal whole number less or equal to the argument. As -14.000000000000002 is not a whole number, the closest whole number downwards is -15.
Well, now let's clear why -0.07 * 200 is not exactly -14. This is because the inner representation of floating-point numbers is in base 2, so the fractions where the denominator is not a power of 2 cannot be represented with 100% precision. (The same way as you cannot represent 1/3 as the decimal fraction with finite amount of decimal places.) So, the value of velocity is not exactly -0.07. (When the compiler sees the constant -0.07, it silently replaces it with a binary fraction which is quite close to -0.07, but not actually equal to.) This is why velocity * 200 is not exactly -14.
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.
If you need numbers that exactly add up to specific expected values, you cannot use double. Read the linked-to site for details.
Use BigDecimal... The problem above is a well-known rounding problem with the representation schemes used on a computer with finite-memory. The problem is that the answer is repetitive in the binary (that is, base 2) system (i.e. like 1/3 = 0.33333333... with decimal) and cannot be presented correctly. A good example of this is 1/10 = 0.1 which is 0.000110011001100110011001100110011... in binary. After some point the 1s and 0s have to end, causing the perceived error.
Hope you're not working on life-critical stuff... for example http://www.ima.umn.edu/~arnold/disasters/patriot.html. 28 people lost their lives due to a rounding error.
Java doubles follow the IEEE 754 floating-point arithmetic, which can't represent every single real number with infinite accuracy. This round up is normal. You can't get rid of it in the internal representation. You can of course use String.format to print the result.

Categories