Java BigDecimal - explanation needed - java

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?

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.

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.

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);
}
}

Related

Fix for float precision issue without BigDecimal

I'm having an issue with the rounding of a float value.
The following code gives me the following result:
public class ProductOrder {
public static void main(String[] args) {
int q = 48;
float p = 6.95f;
System.out.println(q * p);
}
------
Output: 333.59998
While the expected result should be 333.6
When I replace q with 49 then its okay and i get 340,55
As already pointed out, BigDecimal is designed for situations in which exact handling of short decimal fractions is especially important, and is much better for those situations than any binary floating point format.
The second best solution is to work in e.g. integer number of cents, and insert the decimal point only during display.
If you must use binary floating point, double is usually a better choice than float unless you are dealing with a lot of numbers and know float has enough precision.
For output, if you expect a result with e.g. no more than 2 decimal digits after the decimal point, you can use a DecimalFormat to round accordingly:
import java.text.DecimalFormat;
public class Test {
public static void main(String[] args) {
DecimalFormat df = new DecimalFormat("#0.##");
int q = 48;
float p = 6.95f;
System.out.println(df.format(q * p));
}
}
prints 333.6
The default formatting produces the least number of digits that are necessary to tell the number from from its neighbours. This means that if the result is even slightly off because of roundoff error you will get the "ugly" output.
One way to deal with this is using exact arithmetic (BigDecimal or integer arithmetic). Another is rounding the output, for example:
System.out.printf("%f", p*q);
The number 333.6 is not representable exactly as a float, which has up to 6 decimal digits precision. When you tried to print it with too much precision (8 digits) the underlying rounding error was revealed.
double has up to 15 digits precision.
However, as others have pointed out, if you are dealing with financial calculations you should work in BigDecimal.
For reference you can use the IEEE-754 Floating-Point Conversion website to see, for any given decimal number, exactly how it is represented in float and double.
The issue here is that floating points are subject to floating point precision errors. You can use double for twice the precision (you will still eventually get errors, but it is more accurate than using float).
E.g.
public static void main(String[] args) {
int q = 48;
double p = 6.95;
System.out.println(q * p);
}
This will give 333.6.

method to round to a certain amount of decimal places, according to a variable

I currently have a function which will take an input and round it to 4 decimal places, it looks like this:
public static double table_round(double n) {
return (double) Math.round(n * 10000) / 10000;
}
really really simple function, however I was thinking I could change it to allow a second variable to be passed that says how many places to round to, however I'm not sure exactly how to go about printing the correct number of 0's in the math statement there (each 0 represents one decimal place that will be printed). Any ideas on how this could be accomplished? This is just some extra credit stuff for my java class, I'm still learning so I'm sorry if there is a simple solution I'm overlooking.
public static double table_round(double n, int digits) {
return BigDecimal.valueOf(n).setScale(digits,BigDecimal.ROUND_HALF_UP).doubleValue();
}
You can use BigDecimal and its setScale() method to fix no of digits after decimal
public static double table_round(double n) {
// No of digits you want after decimal.
int digitsAfterDecimal = 5;
BigDecimal bigDecimal = BigDecimal.valueOf(n);
// BigDecimal.ROUND_FLOOR is Rounding Mode..denote how your value is rounded off
// Other ways are:- "ROUND_CEILING", "ROUND_DOWN", etc..
bigDecimal = bigDecimal.setScale(digitsAfterDecimal, BigDecimal.ROUND_FLOOR);
return Double.valueOf(bigDecimal.toString());
}
For detailed information, see http://www.opentaps.org/docs/index.php/How_to_Use_Java_BigDecimal:_A_Tutorial

Explanation of output of a java program

I came across the following program
class Boolean {
public static void main(String argv[]) {
boolean x;
x = 4.4f == 4.4;
System.out.println(x);
}
}
The output of the following program is false
But if we write the program in the following fashion, then
class Boolean {
public static void main(String argv[]) {
boolean x;
x = 4.5f == 4.5;
System.out.println(x);
}
}
In this case the output is true
Can somebody explain me why ??
You generally shouldn't compare floating point values with == operator. You should use 'close enough' comparison like checking if values differ by some small value:
double epsilon = 0.000001
boolean equal = Math.abs(value1-value2) < epsilon
In your example, 4.4f is not equal to 4.4, because java defaults floating point values to double type, which is 64bit, and to compare them java casts 4.4f to double, which causes it to be slightly different from original double value 4.4(because of problems representing decimal fractions with binary).
Here's a good link on floating point numbers.
The problem is that computers like numbers to be based on base 2 and not base 10 like us.
4.4 is an infinite fraction (like 0.333333333... for us) in binary, and floats have fewer digits than doubles, so there are fewer digits in 4.4f than in 4.4 making them different.
4.5 is not an infinite fraction.
Note: Whenever you need to compare floats or doubles you should always check the size of the difference, not just check for equality.
Run this code to see how casting of float to double works for these cases
NumberFormat nf = new DecimalFormat("0.00000000000000000000");
System.out.println(nf.format(4.4f));
System.out.println(nf.format(4.4));
System.out.println(nf.format(4.5f));
System.out.println(nf.format(4.5));
That is because of a rounding error when the double gets truncated to a float. Sometimes you get it sometimes you won't.
4.4f is a float and 4.4 is a double.
Your prgramm compares a 16-bit float with 32-bit double value. Internaly it is represented a IEEE754 so the difference is a rounding error which leads in some cases to this inequality due to different precision.
This is down to the fact that a float is a not a double and you can't easily do direct comparison, because a float is just an approximation. Take a look at the following code:
public static void main(String args[]) {
double a, b;
a = 4.4f;
b = 4.5f;
System.out.println("4.4f implicitly cast to a double = "+a);
System.out.println("4.5f implicitly cast to a double = "+b);
}
You'll see that 4.4f, when implicitly cast to a double is in fact 4.400000095367432.
In addition to what everyone has said, here is a very popular example to demonstrate this thing with floating point operations.
System.out.println(0.3 - 0.2 - 0.1);
It won't print 0. In fact, it would print a very small number as a result of the truncation errors that happen in floating point operations when certain fractions are non-terminating repeating in the binary representation.

Java rounding Issue

I have a case, such that, if the float value is 74.126, it is rounded into 74.13 ie. into 2 decimal places. But if the value is 74.125, it should be rounded into 74.12...
Please help me to achieve this kind of rounding methodology
Thanks in advance.
I suggest you use BigDecimal instead of float, as that's a more natural representation where decimal digits make sense.
However, I don't think you actually want the round method, as that deals with precision instead of scale. I think you want setScale, as demonstrated here:
import java.math.*;
public class Test {
public static void main(String[] args) {
roundTo2DP("74.126");
roundTo2DP("74.125");
}
private static void roundTo2DP(String text) {
BigDecimal before = new BigDecimal(text);
BigDecimal after = before.setScale(2, RoundingMode.HALF_DOWN);
System.out.println(after);
}
}
Presumably you are rounding with Math.round(float a)? If so the javadocs explain pretty much how rounding works better than I can here.
You may want to look at the BigDecimal class as it provides a round(MathContext mc) method that allows you to specify different rounding modes such as HALF_DOWN.
Edit:
If you just want to set the number of decimal places you can do it with the setScale method.
float f = 74.125f;
BigDecimal b = new BigDecimal(f).setScale(2, RoundingMode.HALF_DOWN);

Is there any square root function more accurate than java.lang.Math.sqrt(double)

I am looking for square root functions (to use in java) that can give square roots upto atleast to 5-6 decimal places accurately. If there is a way to control accuracy of java.lang.Math.sqrt(double) or any external math library, please mention it.
What problem are you trying to solve? I believe java.lang.Math's sqrt is supposed to be accurate to the full double width. Do you need more than this?
Try out the code at this link, which uses Newton's method to compute square roots for large numbers (and gets BigDecimal output) to see if it fits your needs. If not, you can modify it :) .
http://www.merriampark.com/bigsqrt.htm
You can always implement your own using Newton's Method. Then you can make it as accurate as you want (at the cost of CPU of course).
Numerical Recipes in C has code and in-depth discussion. However make sure you check the license before using their code in your product.
You can use this formula to get it to an arbitrary accuracy:
http://en.wikipedia.org/wiki/Newton's_method#Square_root_of_a_number
I doubt that it will be particularly fast though. You'd also need a way to store numbers with larger than type(double) accuracy.
You could roll your own using BigDecimal and a method like Newton-Raphson. That would allow you to specify a precision.
Have a look at Numerical Recipes if you need any inspiration.
Math.sqrt
public static double sqrt(double a)
Returns the correctly rounded positive square root of a double value. Special cases:
If the argument is NaN or less than zero, then the result is NaN.
If the argument is positive infinity, then the result is positive infinity.
If the argument is positive zero or negative zero, then the result is the same as the argument.
Otherwise, the result is the double value closest to the true mathematical square root of the argument value.
Parameters:
a - a value.
Returns:
the positive square root of a. If the argument is NaN or less than zero, the result is NaN.
This question has been answered thoroughly enough already, but here is an experimental way to verify that Math.sqrt is accurate:
import static java.lang.Math.*;
public class Test {
public static void main(String[] args) {
double max = 0;
for (int i = 0; i < 100; i++) {
double r = random();
double err = abs(pow(sqrt(r), 2) - r) / ulp(r);
if (err > max) max = err;
}
System.out.println(max);
}
}
This prints out 1.0, confirming what the documentation says — that the value returned from sqrt will be within one unit of precision to the exact answer.
public class SquareRoot {
public void sqRoot_Efficient(int n){ //Time Complexity: O(log(n))
for(int i=1;i<=n;i++){
if((n/i)*(n/i) < n){
System.out.println(n/i);
break;
}
}
}
public static void main(String[] args) {
SquareRoot sr=new SquareRoot();
sr.sqRoot_Efficient(27);
}
}
I usually recommend Apache commons as a first place to look for most tasks.. But it appears the commons.math does not include sqrt().

Categories