Java Double Precision: Different formula leads to different result [duplicate] - java

This question already has answers here:
Rounding Errors?
(9 answers)
Is floating point math broken?
(31 answers)
Closed 6 years ago.
Today I find an interesting fact that the formula will influence the precision of the result. Please see the below code
double x = 7d
double y = 10d
​println(1-x/y​)
println((y-x)/y​)​
I wrote this code using groovy, you can just treat it as Java
The result is
1-x/y: 0.30000000000000004
(y-x)/y: 0.3
It's interesting that the two formulas which should be equal have different result.
Can anyone explain it for me?
And can I apply the second formula to anywhere applicable as a valid solution for double precision issue?

To control the precision of floating point arithmetic, you should use java.math.BigDecimal.
You can do something like this.
BigDecimal xBigdecimal = BigDecimal.valueOf(7d);
BigDecimal yBigdecimal = BigDecimal.valueOf(10d);
System.out.println(BigDecimal.valueOf(1).subtract(xBigdecimal.divide(yBigdecimal)));
Can anyone explain it for me?
The float and double primitive types in Java are floating point numbers, where the number is stored as a binary representation of a fraction and a exponent.
More specifically, a double-precision floating point value such as the double type is a 64-bit value, where:
1 bit denotes the sign (positive or negative).
11 bits for the exponent.
52 bits for the significant digits (the fractional part as a binary).
These parts are combined to produce a double representation of a value.
Check this
For a detailed description of how floating point values are handled in Java, follow Floating-Point Types, Formats, and Values of the Java Language Specification.
The byte, char, int, long types are fixed-point numbers, which are exact representions of numbers. Unlike fixed point numbers, floating point numbers will some times (safe to assume "most of the time") not be able to return an exact representation of a number. This is the reason why you end up with 0.30000000000000004 in the result of 1 - (x / y​).
When requiring a value that is exact, such as 1.5 or 150.1005, you'll want to use one of the fixed-point types, which will be able to represent the number exactly.
As I've already showed in the above example, Java has a BigDecimal class which will handle very large numbers and very small numbers.

Related

Exact representation of unrepresentable floating point numbers in Java [duplicate]

This question already has answers here:
Why 0.1 represented in float correctly? (I know why not in result of 2.0-1.9)
(2 answers)
Closed 6 years ago.
This may seem a very silly question, but I encountered something mysterious and seemingly beautiful today. I tried Googling around, and I couldn't dig anything up.
I am aware that 0.1 cannot be represented in binary. And YET, when I run the following code:
double g = 1.0;
System.out.println(g);
g = g/10;
System.out.println(g);
g = g*3;
System.out.println(g);
This produces the output:
1.0
0.1
0.30000000000000004
The first output and the third output are expected, but what is going on with the second one? Why is it, well, correct? This should be impossible, and yet, there it is.
Why is it, well, correct?
As you noted, many decimal floating point numbers cannot be represented as binary floating point numbers and vice versa.
When you write a statement like this:
double g = 0.1;
the decimal value is converted to the nearest binary floating point value. And when you then print it like this
System.out.println(g);
the formatter produces the nearest decimal floating point value according to the following rules:
How many digits must be printed for the fractional part? There must be at least one digit to represent the fractional part, and beyond that as many, but only as many, more digits as are needed to uniquely distinguish the argument value from adjacent values of type double.
(Reference: Double.toString(double) javadoc )
That means that you will often get the exact decimal representation of the decimal number that you started with.
In simple terms, the error in converting from decimal to binary is the same as the error when converting from binary to decimal. The errors "cancel out".
Now, this doesn't always happen. Often the cumulative errors in the calculation are large enough the errors in the decimal (and binary) results will be apparent in the output.
Numeric Promotion Rules
If two values have different data types, Java will automatically promote one of the val-
ues to the larger of the two data types.
If one of the values is integral and the other is floating-point, Java will automatically
promote the integral value to the floating-point value’s data type.
Smaller data types, namely byte, short, and char, are first promoted to int any time
they’re used with a Java binary arithmetic operator, even if neither of the operands is
int.
After all promotion has occurred and the operands have the same data type, the result-
ing value will have the same data type as its promoted operands.
Let us step through the computation line by line:
double g = 1.0;
g is the float64 number representing exactly 1.0.
g = g / 10;
The right operand is converted to double, so it is 10.0 exact.
The division operation is performed at infinite precision (conceptually), and then rounded to the closest float64 number as the result.
The exact answer is clearly 0.1. But the closest float64 number to 0.1 is exactly 7205759403792794 / 256.
Hence g = 0.10000000000000000555111512312578...(more digits). If you want to print the full-precision exact value, look at new BigDecimal(g).
g = g * 3;
Again, the right operand is converted to 3.0 exact. We multiply 0.1000000000000000055511151231257(...) by 3 to get 0.3000000000000000166533453693773(...).
The value of g now is exactly 5404319552844596 / 254.

double operation wrong answer in Java [duplicate]

This question already has answers here:
Is floating point math broken?
(31 answers)
Closed 6 years ago.
I have 2 numbers stored as Double, 1.4300 and 1.4350. When I subtract 1.4350 - 1.4300, it gives me the result: 0.0050000000000001155. Why does it add 1155 to the end and how can I solve this so that it returns 0.005 or 0.0050? I'm not sure rounding will work as I'm working with 2 and 4 decimal numbers.
Oh, I love these... these are caused by inaccuracy in the double representation and floating-point arithmetic is full of these. It is often caused by recurring numbers in binary (i.e. base-2 floating-point representation). For example, in decimal 1/3 = 0.3333' In binary 1/10 is a recurring number, which means it cannot be perfectly represented. Try this: 1 - 0.1 - 0.1 - 0.1 - 0.1. You wont get 0.6 :-)
To solve this, use BigDecimal (preferred) or manipulating the double by first multiplying it something like 10000, then rounding it and then dividing it again (less clean).
Good question... it has caused huge problems in the past. Missiles overshooting targets, satellites crashing after launch, etc. Search the web for some, you'll be amazed!
This is a common pitfall with some computer representations of fractional numbers, see this question or google for floating point precision.
Double is not the right type for very precision floating point calculations, if you want exact results you have to use BigDecimal.

How does java round an integer when stored in a floating point

Its a classic problem: your legacy code uses a floating point when it should really be using n integer. But, its to expensive to change every instance of that variable (or several) in the code. So, you need to write your own rounding function that takes a bunch of parameters to improve accuracy and convert to an integer.
So, the basic questions is, how do floating point numbers round when they are made in java? the classic example is 0.1 what is often quoted as rounding to 0.0999999999998 (or something like that). But does a floating point number always round down to the next value it can represent when given an integer in Java? Does it round down it's internal mantissa to efficiently round down its absolute value? Or does it just pick the value with the smallest error between the integer and the new float?
Also is the behavior different when calling Float.parseFloat(String) when the String is an integer like "1234567890"? And is the behavior also the same when String is a Floating point with more precision than the Float can store.
Please note that I use floating point or reference Float, I use that interchangeably with Double. Same with integer and long.
how do floating point numbers round when they are made in java?
Java truncates (rounds towards zero) when you use the construct (int) d where d has type double or float. If you need to round to the nearest integer, you can use the line below:
int a = (int) Math.round(d);
the classic example is 0.1 what is often quoted as rounding to 0.0999999999998 (or something like that).
The issue you allude to does not exist with integers, which are exactly representable as double (for those between -253 and 253). If the number you are rounding comes from previous computations that should have produced an integer but may not have because of floating-point rounding errors, then (int) Math.round(d) is likely the solution you should use. It means that you will get the correct integer as long as the cumulative error is not above 0.5.
your legacy code uses a floating point when it should really be using n integer. But, its to expensive to change every instance of that variable (or several) in the code.
If the computations producing the double d are only computations +, -, * with other integers, producing intermediate results between -253 and 253, then d automatically contains an integer (it is exact because the floating-point computations involved are exact, and it is an integer because the exact result is an integer), and you can convert it with the simpler (int) d. On the other hand, if division or non-integer operands are involved, then you should not lightly change the type of d, because it would change the results of these computations.
Also is the behavior different when calling Float.parseFloat(String) when the String is an integer like "1234567890"?
This will produce a float whose value is the nearest representable single-precision value to the rational 1234567890. This happens to be 1234567936.0f.
And is the behavior also the same when String is a Floating point with more precision than the Float can store.
Technically, “0.1” is more precision than Float can store. Also, technically, the previous example 1234567890 is also more precision than Float can store. The behavior is the same: Float.parseFloat("0.1") produces the nearest float to the rational number 0.1.

Subtracting two decimal numbers giving weird outputs [duplicate]

This question already has answers here:
Whats wrong with this simple 'double' calculation? [duplicate]
(5 answers)
Closed 9 years ago.
While I was having fun with codes from Java Puzzlers(I don't have the book) I came across this piece of code
public static void main(String args[]) {
System.out.println(2.00 - 1.10);
}
Output is
0.8999999999999999
When I tried changing the code to
2.00d - 1.10d still I get the same output as 0.8999999999999999
For,2.00d - 1.10f Output is 0.8999999761581421
For,2.00f - 1.10d Output is 0.8999999999999999
For,2.00f - 1.10f Output is 0.9
Why din't I get the output as 0.9 in the first place? I could not make any heads or tails out of this? Can somebody articulate this?
Because in Java double values are IEEE floating point numbers.
The work around could be to use Big Decimal class
Immutable, arbitrary-precision signed decimal numbers. A BigDecimal
consists of an arbitrary precision integer unscaled value and a 32-bit
integer scale. If zero or positive, the scale is the number of digits
to the right of the decimal point. If negative, the unscaled value of
the number is multiplied by ten to the power of the negation of the
scale. The value of the number represented by the BigDecimal is
therefore (unscaledValue × 10^-scale).
On a side note you may also want to check Wikipedia article on IEEE 754 how floating point numbers are stored on most systems.
The more operations you do on a floating point number, the more significant rounding errors can become.
In binary 0.1 is 0.00011001100110011001100110011001.....,
As such it cannot be represented exactly in binary. Depending where you round off (float or double) you get different answers.
So 0.1f =0.000110011001100110011001100
And 0.1d=0.0001100110011001100110011001100110011001100110011001
You note that the number repeats on a 1100 cycle. However the float and double precision split it at a different point in the cycle. As such on one the error rounds up and the other rounds down; leading to the difference.
But most importantly;
Never assume floating point numbers are exact
Other answers are correct, just to point to a valid reference, I quote oracle doc:
double: The double data type is a double-precision 64-bit IEEE 754
floating point. Its range of values is beyond the scope of this
discussion, but is specified in the Floating-Point Types, Formats, and
Values section of the Java Language Specification. For decimal values,
this data type is generally the default choice. As mentioned above,
this data type should never be used for precise values, such as
currency

Adding and subtracting doubles are giving strange results [duplicate]

This question already has answers here:
Retain precision with double in Java
(24 answers)
Closed 9 years ago.
So when I add or subtract in Java with Doubles, it giving me strange results. Here are some:
If I add 0.0 + 5.1, it gives me 5.1. That's correct.
If I add 5.1 + 0.1, it gives me 5.199999999999 (The number of repeating 9s may be off). That's wrong.
If I subtract 4.8 - 0.4, it gives me 4.39999999999995 (Again, the repeating 9s may be off). That's wrong.
At first I thought this was only the problem with adding doubles with decimal values, but I was wrong. The following worked fine:
5.1 + 0.2 = 5.3
5.1 - 0.3 = 4.8
Now, the first number added is a double saved as a variable, though the second variable grabs the text from a JTextField. For example:
//doubleNum = 5.1 RIGHT HERE
//The textfield has only a "0.1" in it.
doubleNum += Double.parseDouble(textField.getText());
//doubleNum = 5.199999999999999
In Java, double values are IEEE floating point numbers. Unless they are a power of 2 (or sums of powers of 2, e.g. 1/8 + 1/4 = 3/8), they cannot be represented exactly, even if they have high precision. Some floating point operations will compound the round-off error present in these floating point numbers. In cases you've described above, the floating-point errors have become significant enough to show up in the output.
It doesn't matter what the source of the number is, whether it's parsing a string from a JTextField or specifying a double literal -- the problem is inherit in floating-point representation.
Workarounds:
If you know you'll only have so many decimal points, then use integer
arithmetic, then convert to a decimal:
(double) (51 + 1) / 10
(double) (48 - 4) / 10
Use BigDecimal
If you must use double, you can cut down on floating-point errors
with the Kahan Summation Algorithm.
In Java, doubles use IEEE 754 floating point arithmetic (see this Wikipedia article), which is inherently inaccurate. Use BigDecimal for perfect decimal precision. To round in printing, accepting merely "pretty good" accuracy, use printf("%.3f", x).

Categories

Resources