I am using jdk 1.6. This is my code.
float f = 10.0f;
double d = 10.0;
System.out.println("Equal Status : " + (f == d));
then the system shows the answer as true. But if I modified the value as
float f = 10.1f;
double d = 10.1;
System.out.println("Equal Status : " + (f == d));
then the system shows the answer as false. I know the system use Bit matching for == checking. But what is the reason behind. Can you explain about it? Thanks in advance.
While this is not "my" answer, this is about as close to "must read" literature for programmers who want to move from "meh" to "good." Great is something truly special, so don't think that "good" is anything to sneeze at. :)
What Every Programmer Needs to know about Floating Point
The link #Sam suggested is great but still too technical for me :P
I will just give some opinions to OP for handling floating point (probably a bit off-topic because you are asking for the reason behind. For the reason behind, read the link #Sam suggested).
Never assume floating point number is going to give you accurate representations. Sometimes it can but not always. Floating point has its constraint in "significant figures" which it is "accurate" for the first n-th digit.
Your situation is even worse cause you are mixing float and double, but the idea to solve is similar.
You need to decide to what precision your application needs the calculation result to be, and decide an Epsilon value base on it. For example, your application needs only accuracy to 3 decimal place, probably a Epsilon of 0.0005 is reasonable.
Comparing two floating point number shouldn't be done by ==, you should use
(a + EPSILON > b && a - EPSILON < b). Similarly, a > b should be expressed as a - EPSILON > b
Points to remember are
10.1 is a repeating sequence in binary 1010101010......
When comparing a float and a double the float is converted to a
double by adding zerro's to fill the number out
so you will be comparing
1010101...00000000... to 1010101.....101010... which are different.
float f = 10.1f;
double d = 10.1;
System.out.println("Equal Status : " + (f == (float)d));
will give the answer of true
IMHO, Generally speaking for 99% of use case double is a better choice because it is more accurate. i.e. don't use float unless you have to.
BigDecimal can be used to display the actual representation of a float or double. You don't see this normally as the toString will perform a small amount of rounding (as it is coded to accomodate the types representation limitations)
System.out.println("10.1f is actually " + new BigDecimal(10.1f));
System.out.println("10.1 is actually " + new BigDecimal(10.1));
prints
10.1f is actually 10.1000003814697265625
10.1 is actually 10.0999999999999996447286321199499070644378662109375
You can see that the double value is closer to the desired 10.1 but is not exactly this value. The reason the values are different is that in each case, it have the closest resprentable value for that type.
float is a 32 bit type whereas double is a 64 bit type.
You ran into the classical floating point precision problem.
Floats are imprecise. The actual values of 10.1f and 10.1 will be slightly different due to rounding. Floats are binary, not decimal, so numbers that look "simple" to us, like 10.1, can't be represented exactly as floats.
You would want to refresh yourself on the IEEE floating point standards for both 32 and 64-bit floating point representations. If you peel into the internals of this, you'll see clearly as to why these floating points behave finicky.
If you're curious about how it's represented internally (which is why it's failing), you can use this code, which shows the hexadecimal representations of these numbers. From there, you can match them up with the exponents and mantissas of single and double precision.
System.out.printf("Float 10.0: 0x%X\n", Float.floatToRawIntBits((float)10.0));
System.out.printf("Double 10.0: 0x%X\n", Double.doubleToRawLongBits(10.0));
System.out.printf("Float 10.1: 0x%X\n", Float.floatToRawIntBits((float)10.1));
System.out.printf("Double 10.1: 0x%X\n", Double.doubleToRawLongBits(10.1));
prints
Float 10.0: 0x41200000
Double 10.0: 0x4024000000000000
Float 10.1: 0x4121999A
Double 10.1: 0x4024333333333333
You'll notice that there is some repetition in the way the values are represented. This is because 1/10 can't be represented in a finite space of base 2.
Related
I understand that due to the nature of a float/double one should not use them for precision important calculations. However, i'm a little confused on their limitations due to mixed answers on similar questions, whether or not floats and doubles will always be inaccurate regardless of significant digits or are only inaccurate up to the 16th digit.
I've ran a few examples in Java,
System.out.println(Double.parseDouble("999999.9999999999");
// this outputs correctly w/ 16 digits
System.out.println(Double.parseDouble("9.99999999999999");
// This also outputs correctly w/ 15 digits
System.out.println(Double.parseDouble("9.999999999999999");
// But this doesn't output correctly w/ 16 digits. Outputs 9.999999999999998
I can't find the link to another answer that stated that values like 1.98 and 2.02 would round down to 2.0 and therefore create inaccuracies but testing shows that the values are printed correctly. So my first question is whether or not floating/double values will always be inaccurate or is there a lower limit where you can be assured of precision.
My second question is in regards to using BigDecimal. I know that I should be using BigDecimal for precision important calculations. Therefore I should be using BigDecimal's methods for arithmetic and comparing. However, BigDecimal also includes a doubleValue() method which will convert the BigDecimal to a double. Would it be safe for me to do a comparison between double values that I know for sure have less than 16 digits? There will be no arithmetic done on them at all so the inherent values should not have changed.
For example, is it safe for me to do the following?
BigDecimal myDecimal = new BigDecimal("123.456");
BigDecimal myDecimal2 = new BigDecimal("234.567");
if (myDecimal.doubleValue() < myDecimal2.doubleValue()) System.out.println("myDecimal is smaller than myDecimal2");
Edit: After reading some of the responses to my own answer i've realized my understanding was incorrect and have deleted it. Here are some snippets from it that might help in the future.
"A double cannot hold 0.1 precisely. The closest representable value to 0.1 is 0.1000000000000000055511151231257827021181583404541015625. Java Double.toString only prints enough digits to uniquely identify the double, not the exact value." - Patricia Shanahan
Sources:
https://stackoverflow.com/a/5749978 - States that a double can hold up to 15 digits
I suggest you read this page:
https://en.wikipedia.org/wiki/Double-precision_floating-point_format
Once you've read and understood it, and perhaps converted several examples to their binary representations in the 64 bit floating point format, then you'll have a much better idea of what significant digits a Double can hold.
As a side note, (perhaps trivial) a nice and reliable way to store a known precision of value is to simply multiply it by the relevant factor and store as some integral type, which are completely precise.
For example:
double costInPounds = <something>; //e.g. 3.587
int costInPence = (int)(costInPounds * 100 + 0.5); //359
Plainly some precision can be lost, but if a required/desired precision is known, this can save a lot of bother with floating point values, and once this has been done, no precision can be lost by further manipulations.
The + 0.5 is to ensure that rounding works as expected. (int) takes the 'floor' of the provided double value, so adding 0.5 makes it round up and down as expected.
Why this java code returns 61.004999999999995 instead of 61,005 ?? I donĀ“t get it.
System.out.println(105*0.581);
It occurs due to the nature of floating point numbers . Computers are not very intelligent working with floating point numbers , so we have to work based on approximations.
Instead of 6.005 == 6.004999 , you should do this: 6.005 - 6.004999 < = 0.001
You fall into a floating point precision problem. In computer science there is a simple (but anoing) fact : you cannot represent all real numbers. It's also true for Java.
If you want to go deeper, you can study how floating point number are stores in memory. Key words are : bit of sign; mantissa and exponent. Be aware that the precision also depends on the system memory (32or64)
http://en.wikipedia.org/wiki/Single-precision_floating-point_format
Java speaking, for more precision you can use BigDecimal :
System.out.println(new BigDecimal(105).multiply(new BigDecimal(0.581));
You can also round it with round(MathContext mc) which in this case will give you 61.005 if you set the precision to 5.
System.out.println(new BigDecimal(105).multiply(new BigDecimal(0.581)).round(new MathContext(5)));
https://docs.oracle.com/javase/8/docs/api/java/math/BigDecimal.html
If it's just a question about how to display it and the precision dosen't matter, you can use the DecimalFormat.
System.out.println(new DecimalFormat("###.###").format(105*0.581));
https://docs.oracle.com/javase/8/docs/api/java/text/DecimalFormat.html
In java following expression results into
new Double(1.0E22) + new Double(3.0E22) = 4.0E22
but
new Double(1.0E22) + new Double(4.0E22) = 4.9999999999999996E22
I was expecting it to be 5.0E22. The Double limit is 1.7976931348623157E308.
Appreciate your help. My machine's architecture is x64 and JVM is also 64 bit.
Welcome to the planet of floating point units. Unfortunately, in a real world, you have to give up some precision to get speed and breadth of representation. You cannot avoid that: double is only an approximate representation. Actually, you cannot represent a number but with finite precision. Still, it's a good approximation: less than 0.00000000001% error. This has nothing to do with double upper limits, rather with CPU limits, try doing some more math with Python:
>>> 4.9999999999999996 / 5.
1.0
>>> 5. - 4.9999999999999996
0.0
See? As a side note, never check for equality on double, use approximate equality:
if ((a - b) < EPSILON)
Where EPSILON is a very small value. Probably Java library has something more appropriate, but you get the idea.
If you are insterested in some theory, the standard for floating point operations is IEEE754
There are a few ways that you can reduce floating point errors, e.g. pairwise summation and Kahan summation, but neither of these will help you precisely represent a number like 5.0E22 - as Stefano Sanfilippo stated in his answer, that's due to the limits of what you can represent in using floating point, as opposed to a problem with the algorithm used to achieve the answer. To represent 5.0E22 you should either use BigDecimal or else use a library that has a Rational data type, e.g. JScience.
I've got a little Problem with the Math.cos() method. I know, I have to convert the angle to Radians before using Math.cos(). But if I just do:
System.out.println(Math.cos(Math.toRadians(90));
It outputs: 6.123233995736766E-17
Math.sin() is working well.
From trigonometry:
sin x ~= x, for small values of x
sin x = cos x+pi/2
Because pi/2 can't be represented exactly in IEEE-754 Floating point, it means, that it must be off by some value x, i.e it is represented by pi/2 +- x, where x < the least significant bit in the floating point system. Which in this case is 2^-53 = 1.1102e-16.
In this particular case x ~= 6.123233995736766E-17, which is about 55% of the maximum error.
So, it's a rather good result...
See the Javadoc. "The conversion from degrees to radians is generally inexact."
The value is very close to the correct result. It seems like a loss of precision in the floating point operations of transforming degrees to radians.
Standing temporarily on a soapbox, I think people are confused about the concepts of accuracy versus precision. Is this an issue as some have said? Is it a problem? A bug? Or is it an expected behavior of floating point arithmetic?
90 degrees is a number that is representable perfectly as an integer, even though a double. But pi/2 radians is a real number that is not represented exactly, so that representation will be slightly inaccurate. The loss is in accuracy. The fact is, this is expected behavior. We should never trust the least significant bits of a result.
Next, when we compute the value of a trigonometric function, there MAY be an additional loss of accuracy. We don't get exactly the result that we know to be true in a symbolic sense. Thus sin(pi/3) may not be exactly sqrt(3)/2, but then we can't represent sqrt(3)/2 exactly anyway. All of this is expected, and is behavior that should be dealt with by good code, not trusting the LSBs of those numbers.
I notice some issues with the Java float precision
Float.parseFloat("0.0065") - 0.001 // 0.0055000000134110451
new Float("0.027") - 0.001 // 0.02600000000700354575
Float.valueOf("0.074") - 0.001 // 0.07399999999999999999
I not only have a problem with Float but also with Double.
Can someone explain what is happening behind the scenes, and how can we get an accurate number? What would be the right way to handle this when dealing with these issues?
The problem is simply that float has finite precision; it cannot represent 0.0065 exactly. (The same is true of double, of course: it has greater precision, but still finite.)
A further problem, which makes the above problem more obvious, is that 0.001 is a double rather than a float, so your float is getting promoted to a double to perform the subtraction, and of course at that point the system has no way to recover the missing precision that a double could have represented to begin with. To address that, you would write:
float f = Float.parseFloat("0.0065") - 0.001f;
using 0.001f instead of 0.001.
See What Every Computer Scientist Should Know About Floating-Point Arithmetic. Your results look correct to me.
If you don't like how floating-point numbers work, try something like BigDecimal instead.
You're getting the right results. There is no such float as 0.027 exactly, nor is there such a double. You will always get these errors if you use float or double.
float and double are stored as binary fractions: something like 1/2 + 1/4 + 1/16... You can't get all decimal values to be stored exactly as finite-precision binary fractions. It's just not mathematically possible.
The only alternative is to use BigDecimal, which you can use to get exact decimal values.
From the Java Tutorials page on Primitive Data Types:
A floating-point literal is of type float if it ends with the letter F or f; otherwise its type is double and it can optionally end with the letter D or d.
So I think your literals (0.001) are doubles and you're subtracting doubles from floats.
Try this instead:
System.out.println((0.0065F - 0.001D)); // 0.005500000134110451
System.out.println((0.0065F - 0.001F)); // 0.0055
... and you'll get:
0.005500000134110451
0.0055
So add F suffixes to your literals and you should get better results:
Float.parseFloat("0.0065") - 0.001F
new Float("0.027") - 0.001F
Float.valueOf("0.074") - 0.001F
I would convert your float to a string and then use BigDecimal.
This link explains it well
new BigDecimal(String.valueOf(yourDoubleValue));
Dont use the BigDecimal double constructor though as you will still get errors
Long story short if you require arbitrary precision use BigDecimal not float or double. You will see all sorts of rounding issues of this nature using float.
As an aside be very careful not to use the float/double constructor of BigDecimal because it will have the same issue. Use the String constructor instead.
Floating point cannot accurately represent decimal numbers. If you need an accurate representation of a number in Java, you should use the java.math.BigDecimal class:
BigDecimal d = new BigDecimal("0.0065");