Reputation: 596
I was using BigDecimal, but I am still getting different results for two different (mathematically identical) expressions:
First Expression: PI - (10^(-14)/PI)
Second Expression: (PI^2 - 10^(-14))/PI
To put it more simply, here is the equation:
package set1;
import java.math.BigDecimal;
import java.math.RoundingMode;
public class FloatingLaws {
final static BigDecimal PI = BigDecimal.valueOf(Math.PI);
public static void main(String[] args) {
System.out.println(firstExpression());
System.out.println(secondExpression());
}
private static BigDecimal secondExpression() {
return PI.subtract((BigDecimal.valueOf(Math.pow(10, -14)).divide(PI,50,RoundingMode.CEILING)));
}
private static BigDecimal firstExpression() {
return (PI.multiply(PI).subtract(BigDecimal.valueOf(Math.pow(10, -14)))).divide(PI, 50,RoundingMode.CEILING);
}
}
After executing this code, no matter how big is rounding, last digit is always different. In my case I get these two results:
3.14159265358978981690113816209304300915191180404867
3.14159265358978981690113816209304300915191180404866
My question is why is this happening and is it solvable?
Upvotes: 3
Views: 441
Reputation: 6414
It is because You are doing this:
pi - ((10^-4)/pi)
<- only the part in bracket is ceiled,
which is different from
((pi^2-10^-14)/pi)
<- where the whole expression is ceiled.
You use BigDecimal and you have rounding mode CEILING with precision 50. In both of your expressions the ceiling is applied when you divide by PI number. So if you divide by PI eager like in first expression, then you could get less accurate results - because you CEIL intermediate value, before your formula fully execute, so you loose the CEILED part from the divide by PI operation and this in further computation creates the "error" effect. When you divide by PI last, like in second expression, you use more accurate formula, which is ceiling only the result, not intermediate value like in first expression, so it calculates more preciselly, rounding only the result and not intermediate value.
Upvotes: 5
Reputation: 347
I modified your program to try all rounding modes java knows. Running under oracle jdk 8.72, i get equal results for the rounding modes HALF_UP, HALF_DOWN and HALF_EVEN. But Krzysztof is right, since you are not rounding in the same places, errors are bound to pop up.
public class FloatingLaws {
final static BigDecimal PI = BigDecimal.valueOf(Math.PI);
public static void main(String[] args) {
for (RoundingMode roundingMode : RoundingMode.values()) {
System.out.println(roundingMode);
System.out.println("Equal? "+firstExpression(roundingMode).equals(secondExpression(roundingMode)));
System.out.println(firstExpression(roundingMode));
System.out.println(secondExpression(roundingMode));
}
}
private static BigDecimal secondExpression(RoundingMode roundingMode) {
return PI.subtract((BigDecimal.valueOf(Math.pow(10, -14)).divide(PI, 50, roundingMode)));
}
private static BigDecimal firstExpression(RoundingMode roundingMode) {
return (PI.multiply(PI).subtract(BigDecimal.valueOf(Math.pow(10, -14)))).divide(PI, 50, roundingMode);
}
}
Upvotes: 0
Reputation: 100209
The BigDecimal.subtract
method always produces exact difference between two BigDecimal
numbers, without rounding. On the other hand BigDecimal.divide
usually rounds the result. In your case you are use CEILING
rounding mode which rounds up (towards +infinity). When you calculate a-ceil(b/a)
, you are essentially rounding down the whole result (assuming that a
is already rounded), while calculating ceil((a*a-b)/a)
you're rounding up. That's why firstExpression()
is bigger. Were you using HALF_EVEN
rounding, the result would be the same. Were you using FLOOR
mode, the result would be opposite.
Also take a look to what is BigDecimal.valueOf(Math.PI);
:
System.out.println(BigDecimal.valueOf(Math.PI));
> 3.141592653589793
It's not even close to the actual PI number (given the fact that you need 50 digits). You should define PI explicitly like this:
final static BigDecimal PI = new BigDecimal("3.14159265358979323846264338327950288419716939937510");
Now the result is the following:
3.14159265358979005536378154537278750652190194908786
3.14159265358979005536378154537278750652190194908785
Which is quite different from yours one.
Upvotes: 3