Java, Float.parseFloat(), System.out.printf() inconsistency - java

Consider the following code in Java :
String input = "33.3";
float num = Float.parseFloat(input);
System.out.printf("num: %f\n",num);
Why is the output of the code above
num: 33.299999 ?
Shouldn't it be
num: 33.300000 ?
I would really appreciate it if someone can explain this to me.

You're a victim of floating-point error. In base 2, 33.3 is technically a repeating binary(similar to a repeating decimal), as when written as m/n with m and n being integers and gcd(m,n)=1, the prime factors of n are not a subset of the prime factors of 2. This also means that it cannot be written as the sum of a finite number of terms m*(2^n) where m and n are integers.
A similar example happens with 7/6 in base 10.
_
1.16
becomes
1.16666667
which is then read literally, and is not equal to 7/6.

Don't use float if you can avoid it. The problem here is that %f is treating it as a double when it doesn't have that precision
String input = "33.3";
double num = Double.parseDouble(input);
System.out.printf("num: %f\n",num);
prints
33.300000
This occurs because 33.3f != 33.3 as float has less precision. To see the actual values you can use BigDecimal which covers the actual value precisely.
System.out.println("33.3f = " + new BigDecimal(33.3f));
System.out.println("33.3 = " + new BigDecimal(33.3));
prints
33.3f = 33.299999237060546875
33.3 = 33.2999999999999971578290569595992565155029296875
As you can see the true value represented is slightly too small in both cases. In the case of how %f it shows 6 decimal places even though float is not accurate to 8 decimal places in total. double is accurate to 15-16 decimal places and so you won't see an error unless the value is much larger. e.g. one trillion or more.

The problem is simply that float has finite precision.

32-bit floating point numbers contain enough precision for about 7 decimal places of accuracy.
33.29999 is 7 decimal places.
Change "input" to be 3.3 -- you should see 3.300000
Change "input" to be 333.3 -- you will see something like :333.299988
Using a 64-bit floating point number will give you more precision (15-17 decimal places).

Related

Loss of precision in decimal display of double

Why is there a discrepancy in the display of the following value?
double x = (double)988530483551494912L;
System.out.printf("%f%n", x); -> 988530483551494910.000000
System.out.println(Double.toString(x)); -> 9.8853048355149491E17
System.out.println(new BigDecimal(x)); -> 988530483551494912
As you can see, both toString() and the %f conversion lose the precision of the last digit. However, we can see that the last digit is actually precise, because the BigDecimal conversion preserves it.
Thanks to #user16320675's comment, I'm answering my own question. The reason is that the number 988530483551494912L has precision beyond the limit of the double type's precision, and Double.toString() (and similarly %f) will, as per documentation, only use the minimum number of significant digits required to distinguish a double number from adjacent numbers. Adjacent numbers are those that have the smallest representable difference from the original, on either side of it.
This can be demonstrated using Math.nextAfter to show the adjacent numbers:
import static java.lang.Math.nextAfter;
double x = (double)988530483551494912;
System.out.println(nextAfter(x, Double.MIN_VALUE)); ==> 9.8853048355149478E17
System.out.println(x); ==> 9.8853048355149491E17
System.out.println(nextAfter(x, Double.MAX_VALUE)); ==> 9.8853048355149504E17
So, as we can see, there is no point in adding any more significant figures because this string representation already has enough digits to distinguish the number from adjacent values.
However, a question still remains: it shows 17 significant figures, but 16 would be sufficient. I'm not sure why it issues an extra final digit.

Logarithm-based solution calculates incorrect number of digits in large integers

I don't have enough reputation points yet to leave comments, but saw numerous times when people (incorrectly) suggest using log10 to calculate the number of digits in a positive integer. This is wrong for large numbers!
long n = 99999999999999999L;
// correct answer: 17
int numberOfDigits = String.valueOf(n).length();
// incorrect answer: 18
int wrongNumberOfDigits = (int) (Math.log10(n) + 1);
// also incorrect:
double wrongNumberOfDigits2 = Math.floor(Math.log10(n) + 1);
The logarithm-based solutions will incorrectly output 18 instead of 17.
I'd like to understand why.
Way to get number of digits in an int?
Fastest way to get number of digits on a number?
The problem is that 99999999999999999 cannot be exactly represented as a (double precision) floating-point value in this case. The nearest value is 1.0E+17 when passed as a double parameter to log10.
The same would be true of the log10(n) value: 16.999999999999999995657... - the nearest value that can be represented is 17.
It is mathematically absolutely correct.
Number of digit of any integer not null (positive !) is log10(n)+1. No doubt !
Problems arise with representations, as pointed by Brett Hale.
So, if you want, no problem, no limit, very accurate calculation :) use BigDecimal.
But simplest: use lengthof String:
Long l=99999999999999999L;
int len=l.toString().length();
If you really want to do calculation, see
that: Logarithm of a BigDecimal
that: BigDecimal to the power of BigDecimal on Java/Android

Java: Is (int) double reliable?

When I perform simple math in java with doubles and other number data types, the double values seem to randomly vary a bit from the supposed result, which might be 5,59999999997 or 6,0000000002 or something. When I cast to int, the double value is obviously rounded down to the next whole number. Does this mean the double could be both 5 or 6? Or does that "5,999999999997" still count as 6 though which would be depending on the binary float value? If not, is there a way to let the negative vary be rounded up, but not lower values from 5,5 to 5,999999999996?
I mean, I dont really want to round the value as described in my last sentence. I'd like to always round down to the next whole number, but I don't want to cause an extra decrement due to wrong double math results.
Converting a double to an int always rounds down. You can round to the nearest whole integer via Math.round(double). The double is varying from what you expect because of floating point error.
If you want to round, you can use the round() method.
double d = 6 +/- some small error
long l = Math.round(d);
Or you can add 0.5 for positive numbers
long l = (long) (d + 0.5);
or
long l = (long) (d + (d < 0 ? -0.5 : 0.5));
I'm not sure I understand the question. Usually when you cast a double to int you add 0.5 to have a nice round.
From the Java Language Specification:
The Java programming language uses round toward zero when converting a floating value to an
integer (ยง5.1.3), which acts, in this case, as though the number were truncated, discarding
the mantissa bits. Rounding toward zero chooses at its result the format's value closest to
and no greater in magnitude than the infinitely precise result.
So 5,999999999997 when casted to an int will 5 and 6,0000000002 will be 6. If I understand what you are asking with having negative versions of the values (e.g. -5.97), I fail to see how
Math.round() does not suffice you. -6,0000000002 will be rounded to -6 as will -5,999999999997 and every other value above (but not including) -5.5.

Java sum of all double does not return expected result [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Moving decimal places over in a double
Why is the following sum of numbers not equal to 0.4622? but 0.46219999999999994
Double total = new Double(0.08) + new Double(0.0491) + new Double(0.3218) +
new Double(0.0113) + new Double(0.0); // = 0.46219999999999994
I have an application that checks the users input.
The user inputs 5 decimal numbers and a total number. The application checks if the sum of all 5 numbers capped at 4 decimals behind the komma is equal to the total number.
Capping it gives me 0.4621 which is not equal to 0.4622. I can't use DecimalFormat because it rounds it up. And if i explicitly say, round down then it will fail for this situation.
Any suggestion for how I can solve this?
Try with java.math.BigDecimal. Double rounds result. You will just have to use add method, not + operator.
Avoid using float and double if exact answers are required-- Item 48 -- Effective Java Second edition
Use BigDecimal instead.
Looks like a classic case of floating point arithmetic. If you want exact calculations, use java.math.BigDecimal. Have a look at What Every Computer Scientist Should Know About Floating-Point Arithmetic
When you use floating point arithmetic you must also use appropriate rounding.
BTW: Don't use an object when a primitive will do.
double total = 0.08 + 0.0491 + 0.3218 + 0.0113 + 0.0;
System.out.printf("%.4f%n", total);
double rounded = Math.round(total * 1e4) / 1e4;
if (rounded == 0.4622)
System.out.println("rounded matched");
prints
0.4622
rounded matched
as expected.
Double and float in Java are internally represented as binary fractions and can therefore be not precise in representing decimal fractions (IEEE standard 754). If your decimal number calculations require precision, use Java.math.BigDecimal.
Floating point representation is a close approximation so you will have these little rounding errors when you use float and double. If you try to convert 0.08 to binary for instance you will realize that you cannot actually do it exactly. You need to consider this whenever you use double and float in calculations.
0.0810 = 0.00010100011110101110...2
a repeating pattern. So no matter how many bits you use this will have a rounding error.
That is yet another rounding issue. You should never compare doubles and expect them to be exactly equal. Instead define a small epsylon and expect the result to be within epsylon of the expected answer.
Any floating point value is inexact. The solution is to use DecimalFormat when you have to display the values. And no, it doesn't round up but to the nearest value.
From the javadoc :
DecimalFormat uses half-even rounding (see ROUND_HALF_EVEN) for
formatting.
The internal representation of floating point numbers like Double is never a exact one. This is why during calculations such errors can occur.
It is always suggested to format such a result to a specific number of digits past the comma, so you result would be correctly be display as "0.4622" with 4 to 15 or more digits.
Perhaps checking the string input directly would be more feasible for you. That is check the length of characters after the decimal place.

Weird Java behavior: How come adding doubles with EXACTLY two decimal places result to a double with MORE THAN two decimal places?

If I have an array of doubles that each have EXACTLY two decimal places, add them up altogether via a loop, and print out the total, what comes out is a number with MORE THAN two decimal places. Which is weird, because theoretically, adding two numbers that each have 2 and only 2 decimal places will NEVER produce a number that has a non-zero digit beyond the hundredths place.
Try executing this code:
double[] d = new double[2000];
for (int i = 0; i < d.length; i++) {
d[i] = 9.99;
}
double total = 0,00;
for (int i = 0; i < d.length; i++) {
total += d[i];
if (("" + total).matches("[0-9]+\\.[0-9]{3,}")) { // if there are 3 or more decimal places in the total
System.out.println("total: " + total + ", " + i); // print the total and the iteration when it occured
}
}
In my computer, this prints out:
total: 59.940000000000005, 5
If I round off the total to two decimal places then I'd get the same number as I would if I manually added 9.99 six times on a calculator. But how come this is happening and where are the extra decimal places coming from? Am I doing something wrong or (I doubt this is likely) is this a Java bug?
Are you familiar with base 10 to base 2 conversion (decimal to binary) for fractions? If not, look it up.
Then you'll see that although 9.99 looks pretty normal in base 10, it doesn't really look that nice in binary; It looks like a repeating decimal, but in binary. I'm sure you've seen a repeating decimal before, right? It doesn't end. But Java (or any language for that matter) has to save that infinite sequence of digits into a limited number of bytes. And that's when the extra digits appear. When you convert that truncated binary back to decimal, you're really dealing with a different number. The number stored in the variable isn't 9.99 exactly, it something like 9.9999999991 (just an example, I didn't work out the math).
But you're probably interested on how to solve this, right? Look up the BigDecimal class. That's what you want to use for your calculations, especially when dealing with currency. Also, look up DecimalFormat, which is a class for writing a number as a properly formatted string. I think it does rounding for you when you want to show only 2 decimal digits and your number has a lot more, for example.
If I have an array of doubles that each have EXACTLY two decimal places
Let's stop right there, because I suspect you don't. For example, you give 9.99 in your sample code. That isn't really 9.99. That's "the closest double to 9.99" as 9.99 itself can't be exactly represented in binary floating point.
At that point, the rest of your reasoning goes out of the window.
If you want values with an exact number of decimal digits, you should use a type which stores values in a decimal-centric manner, such as BigDecimal. Alternatively, store everything as integers and "know" that you're actually remembering "the value * 100" instead.
Doubles are represented in a binary format on the computer (). This means that certain numbers cannot be represented accurately, so the computer will use the closest number that can be represented.
E.g. 10.5 = 2^3+2+2^(-1) = 1.0101 * 2^3 (here the mantissa is in binary)
but 10.1 = 2^3+2+2^(-4)+2^(-5)+(infinite series here) = 1.0100001... * 2^3
9.99 is such a number with infinite representation. Thus when you add them together, the finite representation used by the computer is used in the calculation and the result will be even more further away from the mathematical sum than the originals were from their true representation. This is why you see more digits displayed than used in the original numbers.
this is because of floating point arithmetics.
doubles and floats are not exactly real numbers, there are finite number of bits to represent them while there are infinite number of real numbers [in any range], so you cannot represent all real numbers - You are getting the closest number you can have with the floating point representation.
Whenever you deal with floating points - remember that they are only an approximation to the number you are seeking. You might want to use BigDecimal if you want the exact number [or at least control the error].
More info can be found at this article
Use BigDecimal to perform floating point calculations with precision. It's a must when it comes to money.
This is a known issue that stems in the fact that binary calculations don't allow for precise floating point operations. Look at "floating point arithmetics" for more details.
This is due to inaccuracies when it comes to representing decimal numbers using a binary floating point value. In other words, the double literal 0.99 does not actually represent the mathematical value 9.99.
To reveal exactly what number a value, such as 9.99 represents you could let BigDecimal print the value.
Code to reveal the exact value:
System.out.println(new BigDecimal(9.99));
Output:
9.9900000000000002131628207280300557613372802734375
Btw, your reasoning would be completely accurate if you were taking about binary places instead of decimal places, since a number with two binary places can be exactly represented by a binary floating point value.

Categories