Trying to limit number of decimal places for BigDecimal division Java - java

I am new to the Java world and am trying to learn how to use BigDecimal. What I am trying to do now is limit the number of decimal places in a division problem. My line of code is:
quotient=one.divide(x);
Where quotient, one and x are all of type BigDecimal. I cannot figure out, however, how to limit the number of decimal places to print out, saying that x is some large number, and one is equal to 1. All help is appreciated, thanks!

That code will die a horrible death if the division has a non-terminating decimal expansion. See javadoc of divide(BigDecimal divisor):
if the exact quotient cannot be represented (because it has a non-terminating decimal expansion) an ArithmeticException is thrown.
Example:
BigDecimal one = BigDecimal.ONE;
BigDecimal x = BigDecimal.valueOf(7);
one.divide(x); // throws java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
Use one of the other overloads of divide(), e.g. divide(BigDecimal divisor, int scale, RoundingMode roundingMode):
BigDecimal one = BigDecimal.ONE;
BigDecimal x = BigDecimal.valueOf(7);
BigDecimal quotient = one.divide(x, 5, RoundingMode.HALF_UP);
System.out.println(quotient); // prints: 0.14286
BigDecimal one = BigDecimal.ONE;
BigDecimal x = BigDecimal.valueOf(7);
BigDecimal quotient = one.divide(x, 30, RoundingMode.HALF_UP);
System.out.println(quotient); // prints: 0.142857142857142857142857142857

To set the number of decimal places in a variable BigDecimal you can use the following sentences depend that you want achieve
value = value.setScale(2, RoundingMode.CEILING) to do 'cut' the part after 2 decimals
or
value = value.setScale(2, RoundingMode.HALF_UP) to do common round
See Rounding BigDecimal to *always* have two decimal places

Related

Java's Bigdecimal.divide and rounding

At work, we found a problem when trying to divide a large number by 1000. This number came from the database.
Say I have this method:
private static BigDecimal divideBy1000(BigDecimal dividendo) {
if (dividendo == null) return null;
return dividendo.divide(BigDecimal.valueOf(1000), RoundingMode.HALF_UP);
}
When I make the following call
divideBy1000(new BigDecimal("176100000"))
I receive the expected value of 176100. But if I try the line below
divideBy1000(new BigDecimal("1761e+5"))
I receive the value 200000. Why this occurs? Both numbers are the same with different representation and the latest is what I receive from database. I understand that, somehow, the JVM is dividing the number 1761 by 1000, rounding up and filling with 0's at the end.
What is the best way to avoid this kind of behavior? Keep in mind that the original number is not controlled by me.
As specified in javadoc, a BigDecimal is defined by an integer value and a scale.
The value of the number represented by the BigDecimal is therefore
(unscaledValue × 10^(-scale)).
So BigDecimal("1761e+5") has scale -5 and BigDecimal(176100000) has scale 0.
The division of the two BigDecimal is done using the -5 and 0 scales respectively because the scales are not specified when dividing. The divide documentation explains why the results are different.
divide
public BigDecimal divide(BigDecimal divisor)
Returns a BigDecimal whose value is (this / divisor), and whose preferred scale is (this.scale() - divisor.scale()); if the exact quotient cannot be represented (because it has a non-terminating decimal expansion) an ArithmeticException is thrown.
Parameters:
divisor - value by which this BigDecimal is to be divided.
Returns:
this / divisor
Throws:
ArithmeticException — if the exact quotient does not have a terminating decimal expansion
Since:
1.5
If you specify a scale when dividing, e.g. dividendo.divide(BigDecimal.valueOf(1000), 0, RoundingMode.HALF_UP) you will get the same result.
The expressions new BigDecimal("176100000") and new BigDecimal("1761e+5") are not equal. BigDecimal keeps track of both value, and precision.
BigDecimal("176100000") has 9 digits of precision and is represented internally as the BigInteger("176100000"), multiplied by 1. BigDecimal("1761e+5") has 4 digits of precision and is represented internally as the BigInteger("1761"), multiplied by 100000.
When you a divide a BigDecimal by a value, the result respects the digits of precision, resulting in different outputs for seemingly equal values.
for your division with BigDecimal.
dividendo.divide(divisor,2,RoundingMode.CEILING)//00.00 nothing for up and nothing for down
in this operation have a precision for two decimals.
To avoid this kind of problems in Java when dividing by powers of 10 you have a much efficient and precise approach:
dividendo.movePointLeft(3)
Yeah, that's kind of issue what you're experimenting. If I may, in a situation where you only have exponental numbers, you should cast them and then use your method. See what I suggest is this bit of code down there:
long longValue = Double.valueOf("1761e+5").longValue();
BigDecimal value= new BigDecimal(longValue);
Use it in a method which would convert those string into a new BigDecimal and return this BigDecimal value. Then you can use those returned values with divideBy1000.That should clear any issue you're having.
If you have a lot of those, what you can do also in store those BigDecimal in a data structure like a list. Then use a foreach loop in which you apply divideBy1000 and each new value would be stored in a different list. Then you would just have to access this list to have your new set of values !
Hope it helps :)
Try using round().
private static BigDecimal divideBy1000(BigDecimal dividendo) {
if (dividendo == null) return null;
return dividendo.divide(BigDecimal.valueOf(1000)).round(new MathContext(4, RoundingMode.HALF_UP));
}
public static void main(String []args){
BigDecimal bigD = new BigDecimal("1761e5");
BigDecimal bigDr = divideBy1000(bigD);
System.out.println(bigDr);
}
The new MathContext(4, RoundingMode.HALF_UP)) line returns the division to 4 places.
This produces:
1.761E+5
Which is what you want. (:
Any time you are multiplying a BigDecimal by a power of 10, in this case you are multiplying by 10-3, you can use dividendo.scaleByPowerOfTen(power) which only modifies the scale of the BigDecimal object and side steps any rounding issues, or at least moves them to a later calculation.
The other answers here cover the more general case of dividing by any number.
I want to quote basic concepts for BigDecimal:
A BigDecimal consists of an arbitrary precision integer unscaled value and a 32-bit integer scale.
public class BigDecimal extends Number implements Comparable<BigDecimal> {
// …
private final BigInteger intVal;
private final int scale;
}
That is, BigDecimal number is represented as unscaled integer value * 10^(-scale)
For example, 1.234 = 1234 * 10^(-3). So, precision is 4, scale is 3.
Please refer to basic concept in here.
For the former:
BigDecimal bd1 = new BigDecimal("176100000");
System.out.println(bd1.precision()); // 9
System.out.println(bd1.scale()); // 0
BigDecimal bd2 = BigDecimal.valueOf(1000);
System.out.println(bd2.precision()); // 4
System.out.println(bd2.scale()); // 0
System.out.println(bd1.divide(bd2)); // 176100
System.out.println(bd1.divide(bd2, RoundingMode.HALF_UP)); // 176100
BigDecimal result = bd1.divide(bd2);
System.out.println(result.precision()); // 6
System.out.println(result.scale()); // 0
The new BigDecimal("176100000")'s precision is 9 and scale is 0.
The BigDecimal.valueOf(1000)'s precision is 4 and scale is 0.
(176100000 * 10^0) / (1000 * 10^0) = 176100 * 10^0.
With method public BigDecimal divide​(BigDecimal divisor, RoundingMode roundingMode), we have to use the dividend(new BigDecimal("176100000"))'s scale as a scale of returning BigDecimal. In this case, the scale is 0.
Returns a BigDecimal whose value is (this / divisor), and whose scale is this.scale().
As a result, we have BigDecimal number 176100 * 10^0 whose precision is 6 and scale is 0.
The rounding is applied, but the result is integer already, so we just get 176100.
For the latter:
BigDecimal bd1 = new BigDecimal("1761e+5");
System.out.println(bd1.precision()); // 4
System.out.println(bd1.scale()); // -5
BigDecimal bd2 = BigDecimal.valueOf(1000);
System.out.println(bd2.precision()); // 4
System.out.println(bd2.scale()); // 0
System.out.println(bd1.divide(bd2)); // 1.761E+5
System.out.println(bd1.divide(bd2, RoundingMode.HALF_UP)); // 2E+5
BigDecimal result1 = bd1.divide(bd2);
System.out.println(result1.precision()); // 4
System.out.println(result1.scale()); // -2
BigDecimal result2 = bd1.divide(bd2, RoundingMode.HALF_UP);
System.out.println(result2.precision()); // 1
System.out.println(result2.scale()); // -5
The new BigDecimal("1761e+5")'s precision is 4 and scale is -5.
The BigDecimal.valueOf(1000)'s precision is 4 and scale is 0.
(1761 * 10^(-(-5))) / (1000 * 10^0) = 1.761 * 10^(-(-5))
= 1761 * 10^(-(-2)) whose precision is 4 and scale is -2; prints "1.761E+5" using scientific notation of overriden toString.
If we apply rounding, 1.761 * 10^(-(-5)) = 2 * 10^(-(-5)) whose precision is 1 and scale is -5; prints "2E+5" using scientific notation of overriden toString.
I am might be wrong. If you could catch my mistakes, please comment to this answer. I'll correct them.

Rounding double to 2 decimal places returns always 0.0

I try to round a double value to 2 decimal places but I always get 0.0. I tried three approaches.
First:
System.out.println(Math.round(reward*100.0)/100.0);
Second:
System.out.println(String.valueOf(round(reward, 2)));
private double round(double value, int places)
if (places < 0) throw new IllegalArgumentException();
BigDecimal bd = new BigDecimal(value);
bd = bd.setScale(places, RoundingMode.HALF_UP);
return bd.doubleValue();
}
Third:
DecimalFormat df = new DecimalFormat("###.##");
System.out.println(df.format(reward));
Everytime I only get 0.0. Example values:
5.957818181818184E-16 0.0
3.927272727272729E-18 0.0
3.1250000000000005E-16 0.0
1.6000000000000006E-19 0.0
What am I missing here?
Thanks!
The four values you are testing there should be rounded to 0.0!
5.957818181818184E-16 equals 5.95... * 10^(-16) equals 0.000...0595... with 16 zeroes in front of the first non-zero digit. Similar for your other values. Are you familiar with the scientific notation?
All of the examples you've quoted are very, very, very small numbers. If you round them to two places, the result is zero. That's why you're getting the results you're getting; they're correct. The negative exponent with scientific notation tells us that.
For instance, 2E-1 is 0.2. You can write the number out, then move the decimal point to the left the number of times given in the exponent (it would be to the right if the exponent were positive). So your
5.957818181818184E-16
is really:
0.0000000000000005957818181818184
...which rounded to two places is 0.00.

Error with BigDecimal

Some one can say me where is problem please ?
double interval;
BigDecimal diff = BigDecimal.valueOf(17);
int n=39;
BigDecimal N = BigDecimal.valueOf(n);
interval = diff.divide(N).doubleValue();//line26
System.out.println(interval);
I have this error
Exception in thread "main" java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
at java.math.BigDecimal.divide(BigDecimal.java:1603)
at newlogoot.class.main(class.java:26)
Java Result: 1
The problem is that the result of the division is a non-terminating decimal number which can't be fully represented in a BigDecimal as is (as that would require an unlimited amount of memory).
Thus you need to restrict the number of its decimal digits using the two- (or three-) parameter version of BigDecimal.divide, e.g.
interval = diff.divide(N, 3, RoundingMode.HALF_DOWN).doubleValue();//line26
(this rounds the result to 3 decimal digits.)

ArithmeticException: "Non-terminating decimal expansion; no exact representable decimal result"

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)

Double vs. BigDecimal?

I have to calculate some floating point variables and my colleague suggest me to use BigDecimal instead of double since it will be more precise. But I want to know what it is and how to make most out of BigDecimal?
A BigDecimal is an exact way of representing numbers. A Double has a certain precision. Working with doubles of various magnitudes (say d1=1000.0 and d2=0.001) could result in the 0.001 being dropped altogether when summing as the difference in magnitude is so large. With BigDecimal this would not happen.
The disadvantage of BigDecimal is that it's slower, and it's a bit more difficult to program algorithms that way (due to + - * and / not being overloaded).
If you are dealing with money, or precision is a must, use BigDecimal. Otherwise Doubles tend to be good enough.
I do recommend reading the javadoc of BigDecimal as they do explain things better than I do here :)
My English is not good so I'll just write a simple example here.
double a = 0.02;
double b = 0.03;
double c = b - a;
System.out.println(c);
BigDecimal _a = new BigDecimal("0.02");
BigDecimal _b = new BigDecimal("0.03");
BigDecimal _c = _b.subtract(_a);
System.out.println(_c);
Program output:
0.009999999999999998
0.01
Does anyone still want to use double? ;)
There are two main differences from double:
Arbitrary precision, similarly to BigInteger they can contain number of arbitrary precision and size (whereas a double has a fixed number of bits)
Base 10 instead of Base 2, a BigDecimal is n*10^-scale where n is an arbitrary large signed integer and scale can be thought of as the number of digits to move the decimal point left or right
It is still not true to say that BigDecimal can represent any number. But two reasons you should use BigDecimal for monetary calculations are:
It can represent all numbers that can be represented in decimal notion and that includes virtually all numbers in the monetary world (you never transfer 1/3 $ to someone).
The precision can be controlled to avoid accumulated errors. With a double, as the magnitude of the value increases, its precision decreases and this can introduce significant error into the result.
If you write down a fractional value like 1 / 7 as decimal value you get
1/7 = 0.142857142857142857142857142857142857142857...
with an infinite repetition of the digits 142857. Since you can only write a finite number of digits you will inevitably introduce a rounding (or truncation) error.
Numbers like 1/10 or 1/100 expressed as binary numbers with a fractional part also have an infinite number of digits after the decimal point:
1/10 = binary 0.0001100110011001100110011001100110...
Doubles store values as binary and therefore might introduce an error solely by converting a decimal number to a binary number, without even doing any arithmetic.
Decimal numbers (like BigDecimal), on the other hand, store each decimal digit as is (binary coded, but each decimal on its own). This means that a decimal type is not more precise than a binary floating point or fixed point type in a general sense (i.e. it cannot store 1/7 without loss of precision), but it is more accurate for numbers that have a finite number of decimal digits as is often the case for money calculations.
Java's BigDecimal has the additional advantage that it can have an arbitrary (but finite) number of digits on both sides of the decimal point, limited only by the available memory.
If you are dealing with calculation, there are laws on how you should calculate and what precision you should use. If you fail that you will be doing something illegal.
The only real reason is that the bit representation of decimal cases are not precise. As Basil simply put, an example is the best explanation. Just to complement his example, here's what happens:
static void theDoubleProblem1() {
double d1 = 0.3;
double d2 = 0.2;
System.out.println("Double:\t 0,3 - 0,2 = " + (d1 - d2));
float f1 = 0.3f;
float f2 = 0.2f;
System.out.println("Float:\t 0,3 - 0,2 = " + (f1 - f2));
BigDecimal bd1 = new BigDecimal("0.3");
BigDecimal bd2 = new BigDecimal("0.2");
System.out.println("BigDec:\t 0,3 - 0,2 = " + (bd1.subtract(bd2)));
}
Output:
Double: 0,3 - 0,2 = 0.09999999999999998
Float: 0,3 - 0,2 = 0.10000001
BigDec: 0,3 - 0,2 = 0.1
Also we have that:
static void theDoubleProblem2() {
double d1 = 10;
double d2 = 3;
System.out.println("Double:\t 10 / 3 = " + (d1 / d2));
float f1 = 10f;
float f2 = 3f;
System.out.println("Float:\t 10 / 3 = " + (f1 / f2));
// Exception!
BigDecimal bd3 = new BigDecimal("10");
BigDecimal bd4 = new BigDecimal("3");
System.out.println("BigDec:\t 10 / 3 = " + (bd3.divide(bd4)));
}
Gives us the output:
Double: 10 / 3 = 3.3333333333333335
Float: 10 / 3 = 3.3333333
Exception in thread "main" java.lang.ArithmeticException: Non-terminating decimal expansion
But:
static void theDoubleProblem2() {
BigDecimal bd3 = new BigDecimal("10");
BigDecimal bd4 = new BigDecimal("3");
System.out.println("BigDec:\t 10 / 3 = " + (bd3.divide(bd4, 4, BigDecimal.ROUND_HALF_UP)));
}
Has the output:
BigDec: 10 / 3 = 3.3333
BigDecimal is Oracle's arbitrary-precision numerical library. BigDecimal is part of the Java language and is useful for a variety of applications ranging from the financial to the scientific (that's where sort of am).
There's nothing wrong with using doubles for certain calculations. Suppose, however, you wanted to calculate Math.Pi * Math.Pi / 6, that is, the value of the Riemann Zeta Function for a real argument of two (a project I'm currently working on). Floating-point division presents you with a painful problem of rounding error.
BigDecimal, on the other hand, includes many options for calculating expressions to arbitrary precision. The add, multiply, and divide methods as described in the Oracle documentation below "take the place" of +, *, and / in BigDecimal Java World:
http://docs.oracle.com/javase/7/docs/api/java/math/BigDecimal.html
The compareTo method is especially useful in while and for loops.
Be careful, however, in your use of constructors for BigDecimal. The string constructor is very useful in many cases. For instance, the code
BigDecimal onethird = new BigDecimal("0.33333333333");
utilizes a string representation of 1/3 to represent that infinitely-repeating number to a specified degree of accuracy. The round-off error is most likely somewhere so deep inside the JVM that the round-off errors won't disturb most of your practical calculations. I have, from personal experience, seen round-off creep up, however. The setScale method is important in these regards, as can be seen from the Oracle documentation.
If you need to use division in your arithmetic, you need to use double instead of BigDecimal. Division (divide(BigDecimal) method) in BigDecimal is pretty useless as BigDecimal can't handle repeating decimal rational numbers (division where divisors are and will throw java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
Just try BigDecimal.ONE.divide(new BigDecimal("3"));
Double, on the other hand, will handle division fine (with the understood precision which is roughly 15 significant digits)

Categories