Understand mathematical operations using BigDecimal in Java - java

Using Java (JDK 1.8.0_66 and JDK 1.8.0_181) and its built-in BigDecimal class while performing the following operations:
BigDecimal res = new BigDecimal("290");
BigDecimal sub = new BigDecimal("22");
sub = sub.subtract(new BigDecimal("7"));
sub = sub.subtract(new BigDecimal("4"));
res = res.divide(new BigDecimal("22"), 2, RoundingMode.HALF_EVEN);
res = res.multiply(sub);
res = res.setScale(2, RoundingMode.HALF_EVEN);
yields the result of 144.98 while expected is 145.
Why does that happen? I have tried local and an online compiler.
Is setting the scale happening too late? And how can I get a more accurate calculation?
This formula is used to represent monetary value calculations.

The correct answer is 144.98. You compute 290/22 to a presicion of two decimals. The result is 13.18. And 13.18*11 is 144.98
If your computation is "if I were to pay $145 in 11 equal installments, how big should the installments be" you would have to take the difference of 2 cents and add them to the installments.

Related

Error performing division between 2 BigDecimal: java.lang.ArithmeticException: Non-terminating decimal expansion; [duplicate]

Why does the following code raise the exception shown below?
BigDecimal a = new BigDecimal("1.6");
BigDecimal b = new BigDecimal("9.2");
a.divide(b) // results in the following exception.
Exception:
java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
From the Java 11 BigDecimal docs:
When a MathContext object is supplied with a precision setting of 0 (for example, MathContext.UNLIMITED), arithmetic operations are exact, as are the arithmetic methods which take no MathContext object. (This is the only behavior that was supported in releases prior to 5.)
As a corollary of computing the exact result, the rounding mode setting of a MathContext object with a precision setting of 0 is not used and thus irrelevant. In the case of divide, the exact quotient could have an infinitely long decimal expansion; for example, 1 divided by 3.
If the quotient has a nonterminating decimal expansion and the operation is specified to return an exact result, an ArithmeticException is thrown. Otherwise, the exact result of the division is returned, as done for other operations.
To fix, you need to do something like this:
a.divide(b, 2, RoundingMode.HALF_UP)
where 2 is the scale and RoundingMode.HALF_UP is rounding mode
For more details see this blog post.
Because you're not specifying a precision and a rounding-mode. BigDecimal is complaining that it could use 10, 20, 5000, or infinity decimal places, and it still wouldn't be able to give you an exact representation of the number. So instead of giving you an incorrect BigDecimal, it just whinges at you.
However, if you supply a RoundingMode and a precision, then it will be able to convert (eg. 1.333333333-to-infinity to something like 1.3333 ... but you as the programmer need to tell it what precision you're 'happy with'.
You can do
a.divide(b, MathContext.DECIMAL128)
You can choose the number of bits you want: either 32, 64 or 128.
Check out this link :
http://edelstein.pebbles.cs.cmu.edu/jadeite/main.php?api=java6&state=class&package=java.math&class=MathContext
To fix such an issue I have used the below code
a.divide(b, 2, RoundingMode.HALF_EVEN)
Where 2 is scale. Now the problem should be resolved.
I had this same problem, because my line of code was:
txtTotalInvoice.setText(var1.divide(var2).doubleValue() + "");
I change to this, reading previous Answer, because I was not writing decimal precision:
txtTotalInvoice.setText(var1.divide(var2,4, RoundingMode.HALF_UP).doubleValue() + "");
4 is Decimal Precison
AND RoundingMode are Enum constants, you could choose any of this
UP, DOWN, CEILING, FLOOR, HALF_DOWN, HALF_EVEN, HALF_UP
In this Case HALF_UP, will have this result:
2.4 = 2
2.5 = 3
2.7 = 3
You can check the RoundingMode information here: http://www.javabeat.net/precise-rounding-of-decimals-using-rounding-mode-enumeration/
It´s a issue of rounding the result, the solution for me is the following.
divider.divide(dividend,RoundingMode.HALF_UP);
Answer for BigDecimal throws ArithmeticException
public static void main(String[] args) {
int age = 30;
BigDecimal retireMentFund = new BigDecimal("10000.00");
retireMentFund.setScale(2,BigDecimal.ROUND_HALF_UP);
BigDecimal yearsInRetirement = new BigDecimal("20.00");
String name = " Dennis";
for ( int i = age; i <=65; i++){
recalculate(retireMentFund,new BigDecimal("0.10"));
}
BigDecimal monthlyPension = retireMentFund.divide(
yearsInRetirement.divide(new BigDecimal("12"), new MathContext(2, RoundingMode.CEILING)), new MathContext(2, RoundingMode.CEILING));
System.out.println(name+ " will have £" + monthlyPension +" per month for retirement");
}
public static void recalculate (BigDecimal fundAmount, BigDecimal rate){
fundAmount.multiply(rate.add(new BigDecimal("1.00")));
}
Add MathContext object in your divide method call and adjust precision and rounding mode. This should fix your problem
Your program does not know what precision for decimal numbers to use so it throws:
java.lang.ArithmeticException: Non-terminating decimal expansion
Solution to bypass exception:
MathContext precision = new MathContext(int setPrecisionYouWant); // example 2
BigDecimal a = new BigDecimal("1.6",precision);
BigDecimal b = new BigDecimal("9.2",precision);
a.divide(b) // result = 0.17
For me, it's working with this:
BigDecimal a = new BigDecimal("9999999999.6666",precision);
BigDecimal b = new BigDecimal("21",precision);
a.divideToIntegralValue(b).setScale(2)

How do you reduce a java double to n significant digits?

I am looking to print small digits (doubles) for the purpose of printing the errors in using the Newton & Secant methods.
One of my errors is 5.433306166802499E-5
I'd like to print 5.4333E-5
I thought of using BigDecimal but I am not familiar with this class.
I am looking to print small digits (doubles)
System.out.printf("%.4e", 5.433306166802499E-5);
Result: 5.4333e-05
Note: it doesn't reduce the precision of your original value, it just prints it with a lower precision.
double d = 5.433306166802499E-5;
BigDecimal dc = new BigDecimal(d);
dc = dc .round(new MathContext(3)); // desired significant digits
double rounded = dc .doubleValue();
You can indeed use BigDecimal, it would then look as follows:
BigDecimal d = BigDecimal.valueOf(val).setScale(scale, RoundingMode.HALF_UP);
double scaled = d.doubleValue();
Or you could use:
Math.round(val*Math.pow(10, scale))/Math.pow(10, scale);
To answer the general question of reducing a double's significant digits, and not just the case of printing with System.out.printf:
If you're using Java: Double.parseDouble(String.format("%1.4e", 5.433306166802499E-5))
In Scala, you can use the f string interpolator, or the format method:
val a: Double = 5.433306166802499E-5
val reduced1: Double = f"$a%1.4e".toDouble
val reduced2: Double = "%1.4e".format(a).toDouble
Both are equal: 5.4333e-05

Equivalent to bcdiv from php in java

What is the equivalent of bcdiv(string, string [, int]) in java
the function divides two arbitrary precision numbers
I would like for instance to use:
bcdiv(10000,100, 2) results in 100.00
or
bcdiv(10000,100, 3) result in 100.000
I am more interested in the precision it gives.
I would like its equivalent in java
link to the php manual
The BigDecimal class allows arbritary precision math. In particular see the divide method.
BigDecimal a = new BigDecimal(10000);//Can take int
BigDecimal b = new BigDecimal("100");//Or string
BigDecimal answer = a.divide(b);
The number has defined structure, but you can format String produced from this number:
String.format("%.2f",10000/100f);

BigDecimal Error

In Java, I have defined k as
double k=0.0;
I am taking data from database and adding the same using while loop,
while(rst.next()) {
k = k + Double.parseDouble(rst.getString(5));
}
NOTE: In database, I have values as 125.23, 458.45, 665.99 (all two decimals)
When I display k, I get value as
k = 6034.299999999992
Hence I introduced BigDecimal and changed code to below
BigDecimal bd = new BigDecimal(k);
bd = bd.setScale(2,BigDecimal.ROUND_UP);
Now I get new total as bd=6034.30 which is correct.
Problem 1
Well the problem is when I am using same at other place, below is what I am getting
k = 157.3
bd = 157.31
It should have shown bd=157.30 as after adding manually I get 157.30.
Any reason why it is showing as 157.31.
Problem 2
Also any reason why k is showing so many decimal values?
Below are different values I am getting for double variable k
157.3
67.09
1014.6000000000003
229.06999999999996
I don't understand sometime it displays one decimal, sometime it display 2 decimal and most of the time it show 14 decimal value.
Any suggestion would be appreciated.
You're still going via double. Stick to BigDecimal everywhere:
BigDecimal k = BigDecimal.ZERO;
while (rst.next()) {
k = k.add(new BigDecimal(rst.getString(5));
}
Alternatively - and preferrably, if the field in the database is actually a decimal value:
BigDecimal k = BigDecimal.ZERO;
while (rst.next()) {
k = k.add(rst.getBigDecimal(5));
}
As to your second question, double is a binary floating point number. This means that it is expressed as a sum of powers of two. Don't ever use those for calculating monetary values. (if that's what you're summing up there). BigDecimal uses decimal arithmetic, so this is more in line to what we use.
Numbers such as 0.1 are infinite fractions in binary, in this case: 0.000110011... thus you cannot get a reliable and exact result from using double.
I assume rst is a ResultSet. Make sure you are using getBigDecimal rather than Double.parseDouble(rst.getString(5)):
BigDecimal k = BigDecimal.ZERO;
while(rst.next()) {
k = k.add(rst.getBigDecimal(5));
}
And first of all: why aren't you adding these numbers in the database directly using appropriate SQL SUM query?
Use BigDecimal.ROUND_HALF_UP (or .._DOWN or .._EVEN).
Floating point calculations are inherently inaccurate and the small errors accumulate. That's why your end result is off by a small amount. If you always round up, a small positive error like 1.0000000001 becomes 1.01.
Alternatively you can use BigDecimal also for the calculations. That way you won't have an error in the end result in the first place. Just remember to use the BigDecimal(String) constructor, or obtain the BigDecimal directly from the result set.
You need to have k as BigDecimal too, instead of double.

How to resolve a Java Rounding Double issue [duplicate]

This question already has answers here:
Retain precision with double in Java
(24 answers)
Closed 4 years ago.
Seems like the subtraction is triggering some kind of issue and the resulting value is wrong.
double tempCommission = targetPremium.doubleValue()*rate.doubleValue()/100d;
78.75 = 787.5 * 10.0/100d
double netToCompany = targetPremium.doubleValue() - tempCommission;
708.75 = 787.5 - 78.75
double dCommission = request.getPremium().doubleValue() - netToCompany;
877.8499999999999 = 1586.6 - 708.75
The resulting expected value would be 877.85.
What should be done to ensure the correct calculation?
To control the precision of floating point arithmetic, you should use java.math.BigDecimal. Read The need for BigDecimal by John Zukowski for more information.
Given your example, the last line would be as following using BigDecimal.
import java.math.BigDecimal;
BigDecimal premium = BigDecimal.valueOf("1586.6");
BigDecimal netToCompany = BigDecimal.valueOf("708.75");
BigDecimal commission = premium.subtract(netToCompany);
System.out.println(commission + " = " + premium + " - " + netToCompany);
This results in the following output.
877.85 = 1586.6 - 708.75
As the previous answers stated, this is a consequence of doing floating point arithmetic.
As a previous poster suggested, When you are doing numeric calculations, use java.math.BigDecimal.
However, there is a gotcha to using BigDecimal. When you are converting from the double value to a BigDecimal, you have a choice of using a new BigDecimal(double) constructor or the BigDecimal.valueOf(double) static factory method. Use the static factory method.
The double constructor converts the entire precision of the double to a BigDecimal while the static factory effectively converts it to a String, then converts that to a BigDecimal.
This becomes relevant when you are running into those subtle rounding errors. A number might display as .585, but internally its value is '0.58499999999999996447286321199499070644378662109375'. If you used the BigDecimal constructor, you would get the number that is NOT equal to 0.585, while the static method would give you a value equal to 0.585.
double value = 0.585;
System.out.println(new BigDecimal(value));
System.out.println(BigDecimal.valueOf(value));
on my system gives
0.58499999999999996447286321199499070644378662109375
0.585
Another example:
double d = 0;
for (int i = 1; i <= 10; i++) {
d += 0.1;
}
System.out.println(d); // prints 0.9999999999999999 not 1.0
Use BigDecimal instead.
EDIT:
Also, just to point out this isn't a 'Java' rounding issue. Other languages exhibit
similar (though not necessarily consistent) behaviour. Java at least guarantees consistent behaviour in this regard.
I would modify the example above as follows:
import java.math.BigDecimal;
BigDecimal premium = new BigDecimal("1586.6");
BigDecimal netToCompany = new BigDecimal("708.75");
BigDecimal commission = premium.subtract(netToCompany);
System.out.println(commission + " = " + premium + " - " + netToCompany);
This way you avoid the pitfalls of using string to begin with.
Another alternative:
import java.math.BigDecimal;
BigDecimal premium = BigDecimal.valueOf(158660, 2);
BigDecimal netToCompany = BigDecimal.valueOf(70875, 2);
BigDecimal commission = premium.subtract(netToCompany);
System.out.println(commission + " = " + premium + " - " + netToCompany);
I think these options are better than using doubles. In webapps numbers start out as strings anyways.
Any time you do calculations with doubles, this can happen. This code would give you 877.85:
double answer = Math.round(dCommission * 100000) / 100000.0;
Save the number of cents rather than dollars, and just do the format to dollars when you output it. That way you can use an integer which doesn't suffer from the precision issues.
See responses to this question. Essentially what you are seeing is a natural consequence of using floating point arithmetic.
You could pick some arbitrary precision (significant digits of your inputs?) and round your result to it, if you feel comfortable doing that.
This is a fun issue.
The idea behind Timons reply is you specify an epsilon which represents the smallest precision a legal double can be. If you know in your application that you will never need precision below 0.00000001 then what he suggests is sufficient to get a more precise result very close to the truth. Useful in applications where they know up front their maximum precision (for in instance finance for currency precisions, etc)
However the fundamental problem with trying to round it off is that when you divide by a factor to rescale it you actually introduce another possibility for precision problems. Any manipulation of doubles can introduce imprecision problems with varying frequency. Especially if you're trying to round at a very significant digit (so your operands are < 0) for instance if you run the following with Timons code:
System.out.println(round((1515476.0) * 0.00001) / 0.00001);
Will result in 1499999.9999999998 where the goal here is to round at the units of 500000 (i.e we want 1500000)
In fact the only way to be completely sure you've eliminated the imprecision is to go through a BigDecimal to scale off. e.g.
System.out.println(BigDecimal.valueOf(1515476.0).setScale(-5, RoundingMode.HALF_UP).doubleValue());
Using a mix of the epsilon strategy and the BigDecimal strategy will give you fine control over your precision. The idea being the epsilon gets you very close and then the BigDecimal will eliminate any imprecision caused by rescaling afterwards. Though using BigDecimal will reduce the expected performance of your application.
It has been pointed out to me that the final step of using BigDecimal to rescale it isn't always necessary for some uses cases when you can determine that there's no input value that the final division can reintroduce an error. Currently I don't know how to properly determine this so if anyone knows how then I'd be delighted to hear about it.
So far the most elegant and most efficient way to do that in Java:
double newNum = Math.floor(num * 100 + 0.5) / 100;
Better yet use JScience as BigDecimal is fairly limited (e.g., no sqrt function)
double dCommission = 1586.6 - 708.75;
System.out.println(dCommission);
> 877.8499999999999
Real dCommissionR = Real.valueOf(1586.6 - 708.75);
System.out.println(dCommissionR);
> 877.850000000000
double rounded = Math.rint(toround * 100) / 100;
Although you should not use doubles for precise calculations the following trick helped me if you are rounding the results anyway.
public static int round(Double i) {
return (int) Math.round(i + ((i > 0.0) ? 0.00000001 : -0.00000001));
}
Example:
Double foo = 0.0;
for (int i = 1; i <= 150; i++) {
foo += 0.00010;
}
System.out.println(foo);
System.out.println(Math.round(foo * 100.0) / 100.0);
System.out.println(round(foo*100.0) / 100.0);
Which prints:
0.014999999999999965
0.01
0.02
More info: http://en.wikipedia.org/wiki/Double_precision
It's quite simple.
Use the %.2f operator for output. Problem solved!
For example:
int a = 877.8499999999999;
System.out.printf("Formatted Output is: %.2f", a);
The above code results in a print output of:
877.85
The %.2f operator defines that only TWO decimal places should be used.

Categories

Resources