Issue with rounding aftrer division of BigDecimal values. Java - java

I've got a problem with the result of division of BigDecimal instances.
My program:
public class DecimalPrecision
public static void main(String[] args) {
MathContext mathContext = new MathContext(25, RoundingMode.HALF_UP);
System.out.println(new BigDecimal("0.1111111111111111111111111").divide(new BigDecimal("3"), mathContext).toString());
}
The result is the following:
0.03703703703703703703703703
But the correct result is:
0.03703703703703703703703704
Basically my program rounded the last digit, which is 7, to 3, not to 4. Why is it so? In mathContext I specified rounding mode HALF_UP. Probably I've got a gap in knowlegde on the issue but I was sure that HALF_UP rounds to a bigger value.
This problem can be sorted out in the next way:
value1 = value1.divide(value2, 10000, RoundingMode.HALF_UP);
return new BigDecimal(value1.toString()).round(mathContext);
But in my taste there's something wrong with parsing the result to a String and then create new properly rounded BigDecimal. I'm asking for your help, how can I resolve this problem without resorting to any strings?
Thanks in advance.

You are starting with a 25 digit number and performing an operation with a precision of 25 digits so you can expect that the last digit might be wrong. The solution is to use more digits than you need and round the result.
BigDecimal oneNinth25 = BigDecimal.ONE.divide(BigDecimal.valueOf(9), 25, BigDecimal.ROUND_HALF_UP);
BigDecimal oneNinth26 = BigDecimal.ONE.divide(BigDecimal.valueOf(9), 26, BigDecimal.ROUND_HALF_UP);
MathContext mathContext = new MathContext(25, RoundingMode.HALF_UP);
System.out.println("1/9 to 25 digits / 3 = " +
oneNinth25.divide(BigDecimal.valueOf(3), mathContext));
System.out.println("1/9 to 26 digits / 3 = " +
oneNinth26.divide(BigDecimal.valueOf(3), mathContext));
prints
1/9 to 25 digits / 3 = 0.03703703703703703703703703
1/9 to 26 digits / 3 = 0.03703703703703703703703704

Related

Rounding to the nearest hundered-thousandths

I'm facing an issue while trying to round up the decimal places in a number to the nearest hundered-thousandth digit.
Example :
BigDecimal num1 = BigDecimal.valueOf(0.38871551);
MathContext mc = new MathContext(5);
System.out.println(num1.round(mc));
The output is 0.38872 which is as expected. All good so far. Let's take another example :
BigDecimal num1 = BigDecimal.valueOf(1.1680418);
MathContext mc = new MathContext(5);
System.out.println(num1.round(mc));
The output is 1.1680. This is where the problem arrises. What I want is the output to be 1.16804 but the rounding seems to eat up the 4 instead of leaving it as it is.
I tried different rounding modes but this is what I get :
RoundMode.UP gives 1.1681
RoundingMode.HALF_UP or RoundingMode.HALF_DOWN give 1.1680
and so on..
How do I get the desired output :
0.38871551 should round to 0.38872
1.1680418 should round to 1.16804
0.55052984 should round to 0.55053
I even tried rounding to the 6th decimal place instead of the 5th but I'm not able to find the right combination that gives me the desired output as shown above.
You can try it with setScale Function of BigDecimal
num1 = num1.setScale(5, BigDecimal.ROUND_HALF_UP);
MathContext(int) rounds the number to that amount of digits, taking the whole number into account instead of just the digits after the comma.
say you use these numbers:
BigDecimal ten = BigDecimal.valueOf(10.55052984);
MathContext five = new MathContext(5);
MathContext two = new MathContext(2);
System.out.println(ten.round(five));
System.out.println(ten.round(two));
It will produce a number rounded to the 5th or 2nd digit, regardless of its position:
10.551
11
This should work:
BigDecimal num1 = BigDecimal.valueOf(0.38871551);
System.out.println(num1.setScale(5, RoundingMode.HALF_UP));
num1 = BigDecimal.valueOf(1.1680418);
System.out.println(num1.setScale(5, RoundingMode.HALF_UP));
num1 = BigDecimal.valueOf(0.55052984);
System.out.println(num1.setScale(5, RoundingMode.HALF_UP));
0.38872
1.16804
0.55053
you can try this way
DecimalFormat df = new DecimalFormat("#.00000");
System.out.print(df.format(1.1680418)); //1.16804

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.

dividing bigdecimal containing bigger dividers than scale

I want to find a way to scale divisions of bigdecimals that can contain diverse numbers.
if I use this code:
r= x.divide(y,10, RoundingMode.HALF_UP);
the result of almost all results are correct, but if I have a divisor with more digits before point than the scale, the result is 0.0000000000.
What I have to do to obtain the correct precision on these divisions?
1 / 3 =0.3333333333 (scale 10)
1 / 151545545664651878 = 1.515455456646E-17 (for example)
100000000000000 / 3 = 3.3333333333E+14
thanks.
I found the way. It's realizing operations with mathcontext instead of scale.
It seems that MathContext constructor uses precission not scale.
with MathContext
MathContext mc = new MathContext(10, RoundingMode.HALF_UP);
BigDecimal r = x.divide(y, mc);
Log.i("mc","r = " + r);
with Scale
BigDecimal r = x.divide(y,10, RoundingMode.HALF_UP);
Log.i("scale","r = " + r);
If I realize this division :
1 / 3333333333333333333333333 =
The first output : 3.0000000000E-19
And the second output: 0E-10
So, set Scale to a big decimal seems the same of Scale in MathContext but it isn't.
I hope it could helps someone.

How to round the floating point precision, regard to the size of it?

It's easy to round the value to a specific number of decimal positions:
public static double round(double x, int n){
n = (int)Math.pow(n, 10);
return Math.round(x*n)/n;
}
But can you make an algorithm that can presuppose how many decimal places it would actually need to round up depending on how large the number is?
I mean, if the number is huge (like 100000000.12345) it wouldn't need that much of decimal precision so it can round it up to lets say 1 or 2 decimal places,
While if a number is pretty slight (say like 0.00012345) it would need maximum decimal precision
What's the idea to do something like this?
EDIT:
Example:
argument returned decnum comment
123456789.9 123456789 0 //number is way too large so I don't need any decimal places
12345678.99 12345678 0 //number is still very large.
1234567.899 1234567 0 //still...
123456.7899 123456 0 //...
12345.67899 12345 0 //...
1234.567899 1234 0 //It's pretty large now. Still not small enough to get some decimals.
123.4567899 123.4 1 //Now, number is a little average, so 1 decimal place is included.
12.34567899 12.34 2 //The number is normal, so I want a normal 2 decimal places
1.234567899 1.234 3 //Now it's kinda small, so it now gives 3 decimal places
.1234567899 0.1234 4 //Smaller number, thus greater number of decimal places (4).
.0123456789 0.01234 5 //Even smaller
.0012345678 0.0012345 7 //Larger number of decimal places.
.0001234567 0.0001234567 10 //Smaller and smaller number, greater and greater number of decimals.
I'm just looking for a waywhere I can INCREASE number of decimals when the number is getting closer to zero,
And DECREASE the decimal-number as the number gets away from zero.
You could use BigDecimal. Create a BigDecimal with your double value then check the scale with bigDVariable.scale() and round with the wanted scale depending on the value returned by it and its value. BigDecimal doc.
Edit : You don't seem to worry about how many decimals you have in the first place, the value of your double looks to be the only information that matters. Checking scale() wouldn't matter then.
From algorithm point of view, I would follow the steps below:
Convert the number in base 10 format e.g. 123456789.9 -> 1.234567899 * 10^8 and 0.0001234567 --> 1.234567 * 10^-4.
Look at the power factors of 10 e.g. 8 in the first example and -4 in the last example.
Compute the precision place requirement by adjusting a factor. Just an example could be as (-1*(p-3)), where p is the power factor. If the number comes negative then use '0'.
This will result into following numbers.
argument decnum
123456789.9 -1*(8-3) = -5 -> 0
12345678.99 -1*(7-3) = -4 -> 0
1234567.899 -1*(6-3) = -3 -> 0
123456.7899 -1*(5-3) = -2 -> 0
12345.67899 -1*(4-3) = -1 -> 0
1234.567899 -1*(3-3) = 0 -> 0
123.4567899 -1*(2-3) = 1 -> 1
12.34567899 -1*(1-3) = 2 -> 2
1.234567899 -1*(0-3) = 3 -> 3
.1234567899 -1*(-1-3) = 4 -> 4
.0123456789 -1*(-2-3) = 5 -> 5
.0012345678 -1*(-3-3) = 6 -> 6
.0001234567 -1*(-4-3) = 7 -> 7
This would be very close to what you are looking for. Try adjusting the factor and the formula to get more closer, if you really want to (this is just an algorithm/aproach).
If you want high precision (significant digits), you'll have to use BigDecimal instead of double. The BigDecimal round method gives you the number of significant digits you ask for.
Here's something I put together.
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
public class Rounding {
public static void main(String[] args) {
BigDecimal number = new BigDecimal(100000000.12345);
System.out.println("Number: " + number.toPlainString());
System.out.println(" ");
for (int precision = 8; precision < 17; precision++) {
System.out.println("Precision: " + precision + ", result: "
+ round(number, precision));
}
}
public static String round(BigDecimal number, int precision) {
MathContext mathContext = new MathContext(precision,
RoundingMode.HALF_UP);
BigDecimal rounded = number.round(mathContext);
return rounded.toPlainString();
}
}
And here are the results.
Number: 100000000.12344999611377716064453125
Precision: 8, result: 100000000
Precision: 9, result: 100000000
Precision: 10, result: 100000000.1
Precision: 11, result: 100000000.12
Precision: 12, result: 100000000.123
Precision: 13, result: 100000000.1234
Precision: 14, result: 100000000.12345
Precision: 15, result: 100000000.123450
Precision: 16, result: 100000000.1234500
.
Number: 0.00012344999999999999203137424075293893110938370227813720703125
Precision: 8, result: 0.00012345000
Precision: 9, result: 0.000123450000
Precision: 10, result: 0.0001234500000
Precision: 11, result: 0.00012345000000
Precision: 12, result: 0.000123450000000
Precision: 13, result: 0.0001234500000000
Precision: 14, result: 0.00012345000000000
Precision: 15, result: 0.000123450000000000
Precision: 16, result: 0.0001234500000000000

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)

Categories