I am Seeing some strange behavior from BigDecimal
When I do division using mathContext the output is different than when I do the division by directly providing the scale and rounding mode
Here is an example that I think should provide the same output
public static void main(String...args){
MathContext mc = new MathContext(3,RoundingMode.HALF_UP);
BigDecimal four = new BigDecimal(4);
BigDecimal three = new BigDecimal(3);
System.out.println(four.divide(three,3,RoundingMode.HALF_UP));
System.out.println(four.divide(three,mc));
}
Output:
1.333
1.33
It appears that the scale is treated differently when using MathContext. Or I dont understand when to use which.
The divide method of BigDecimal lets you specify the scale of the result, which loosely speaking is number of decimal places. scale = 3 means that a number will be expressed with 3 decimal places. A negative scale indicates the number of insignificant zeroes at the end of a whole number - so for example to round to the nearest 1000, you can specify scale = -3.
four.divide(three,3,RoundingMode.HALF_UP); // scale = 3, so round to 3 decimal places
But a MathContext is different. It lets you specify precision - that is, the number of significant digits. This is different from scale.
MathContext mc = new MathContext(3,RoundingMode.HALF_UP);
four.divide(three, mc); // precision = 3, so round to 3 significant figures
Related
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)
Let us say we have the following numbers:
23.499999959800466
23.49999995949621
23.49999995980162
23.499999956586194
23.499999954013447
23.499999959041133
By looking at them, one can notice the difference in the 9th decimal point. My question is how to identify such a difference in Java?
If you know two numbers have the same integer part, use base 10 log:
double n1 = 23.499999959800466d;
double n2 = 23.49999995949621d;
// index of the last identical decimal place between n1 and n2
int decimalDiff = (int) Math.abs(Math.log10(n1 - n2));
System.out.println(decimalDiff);
Output: 9
If you have many numbers and you know they all have the same integer part, then calculate the decimal difference between the smallest and largest of the list:
List<Double> numbers = Arrays.asList(
23.499999959800466,
23.49999995949621,
23.49999995980162,
23.499999956586194,
23.499999954013447,
23.499999959041133);
double smallest = Collections.min(numbers);
double largest = Collections.max(numbers);
System.out.println((int) -Math.log10(largest - smallest));
Output: 8. The numbers all have 8 decimal places in common.
You can use java.math.BigDecimal class for your solution.
The BigDecimal class provides operations on double numbers for arithmetic, scale handling, rounding, comparison, format conversion and hashing. It can handle very large and very small floating point numbers with great precision but compensating with the time complexity a bit.
A BigDecimal consists of a random precision integer unscaled value and a 32-bit integer scale. If greater than or equal to zero, the scale is the number of digits to the right of the decimal point.strong text.
BigDecimal bigDecimal = new BigDecimal(num);
Java BigDecimal Methods
BigDecimal add(BigDecimal bigDecimal2)
BigInteger bigInt = new BigInteger("233233233233");
BigDecimal bigDecimal = new BigDecimal(bigInt);
BigDecimal bigDecimal2 = new BigDecimal(55662.3);
System.out.println(bigDecimal.add(bigDecimal2));
BigDecimal subtract(BigDecimal bigDecimal2):
bigDecimal.subtract(bigDecimal2)
You can use subtract method of BigDecimal class for you solution and easily compute difference between two BigDecimal number.
BigDecimal setScale(int newScale, RoundingMode roundingMode):
You can also set scale for numbers using setScale function.
UP: to round away from zero
CEILING: to round towards positive infinity
DOWN: to round towards zero
FLOOR: to round towards negative infinity
HALF_DOWN: to round towards “nearest neighbor” unless both neighbors are equidistant, in which case round down
HALF_EVEN: to round towards the “nearest neighbor” unless both neighbors are equidistant, in which case, round towards the even neighbor
HALF_UP: to round towards “nearest neighbor” unless both neighbors are equidistant, in which case round up
UNNECESSARY: to assert that the requested operation has an exact result, hence no rounding is necessary
BigDecimal bigDecimal = new BigDecimal("23323323.3533");
bigDecimal.setScale(2,RoundingMode.CEILING)
bigDecimal.setScale(2,RoundingMode.DOWN)
bigDecimal.setScale(2,RoundingMode.FLOOR)
Output:
23323323.3533
CEILING: 23323323.36
DOWN: 23323323.35
FLOOR: 23323323.35
Hope this will help.
I have this strange issue of having to cap the precision of double numbers to a number of decimal places, or no decimal places at all, where the precision is handed to me like so: 0.001, 0.1, 1, 0.00001, etc.
So I could be given, for example, 1.234247324 and a precision indicator of 0.001, and with that I would need to return 1.234. If Instead I had been handed a precision of 1.0, I would then return 1 (no decimal places), and so forth.
I'm not sure how to go about this. Does anyone has pointers on how to tackle this?
Thank you!
Start with your value, and create a BigDecimal, then round to what you want.
double value = 1.23456; //not actually equal to that, but pretend.
BigDecimal better = new BigDecimal(value);
BigDecimal rounded = better.round(new MathContext(3));
Rounded is now the actual value you want. What can you do with the precision indicator?
BigDecimal precision = new BigDecimal("0.001");
Now you can get the scale of the precision, which you can use for rounding.
System.out.println(precision.scale());
//outputs 3.
That would be similar to using the logarithm.
Use a hybrid approach of the two other answers allows unusual precisions like 0.25 to be specified:
BigDecimal rounded = (x / precision).round(new MathContext(0)) * precision;
Try this code
double x = 1.234247324;
double p = 0.001;
double y = (int)(x * 1 / p) / (1 / p);
Don't really understand what you want to do but this results 1.234.
I have a double that I want to keep only 3 decimal places but without applying any rounding at all.
E.g. 92.36699 should be 92.366
I tried the following:
DecimalFormat nf= new DecimalFormat("#0.000");
String number = nf.format(originalNumber);
But this results in 92.367
How can I do what I need?
This isn't "no rounding", it's DOWN rounding. Simply set the roundingMode.
DecimalFormat nf = new DecimalFormat("#0.000");
nf.setRoundingMode(RoundingMode.DOWN);
String number = nf.format(originalNumber);
Note the difference between FLOOR and DOWN - only relevant for negative numbers. FLOOR rounds towards negative infinity therefore -92.36699 would become "-92.367".
I'm trying to calculate a percentage "factor". That is, given a 20%, convert it into 0.2 (my intention is to later multiply values by that and get the 20% of the values).
Anyway, the question is related with this piece of code:
public static void main(String[] args) {
int roundingMode = BigDecimal.ROUND_FLOOR;
BigDecimal hundred = new BigDecimal("100");
BigDecimal percentageFactor = null;
BigDecimal percentage = new BigDecimal("20");
BigDecimal value = new BigDecimal("500");
percentageFactor = percentage.divide(hundred, roundingMode);
float f = percentage.floatValue() / hundred.floatValue();
f = value.floatValue() * f;
BigDecimal aux = value.multiply(percentageFactor);
System.out.println("factor:"+percentageFactor.toString());
System.out.println("final falue:"+aux.toString());
System.out.println("Float Value:"+f);
}
I would expect the outcome of this to be something like:
factor: 0.2
final value: 100
float value: 100
but instead percentage.divide(hundred, roundingMode); is returning zero, an hence I get:
factor:0
final falue:0
Float Value:100.0
What am I doing wrong? How can I divide two big decimals properly?
By the way, I'm using BigDecimal because I will be calculating monetary percentages, so I want control regarding rounding.
I think that the best solution is to set the requested scale when dividing: In this case perhaps 2.
var hundred = new BigDecimal(100);
var percentage = new BigDecimal(20);
var value = new BigDecimal(500);
var percentageFactor =
percentage.divide(hundred,2, BigDecimal.ROUND_HALF_UP);
value = value.multiply(percentageFactor);
System.out.println("final value:"+ value);
Final value: 100.00
The multiplication is using the scale from the factors (0+2) but it can be specified too.
I'd use ROUND_HALF_UP for accounting (in my legislation) or ROUND_EVEN (for statistics) for rounding mode.
The scale of new BigDecimal("20") is zero because you've got no decimal point in there. That means that your percentage.divide(hundred, BigDecimal.ROUND_FLOOR) will produce zero (it's effectively int(20/100) or 0).
If you really want to do fractional stuff, use new BigDecimal("20.00") so the scale is set correctly, or use one of the other constructors to set the scale specifically.
Here's the output from that simple change of 20 to 20.00, complete with your spellink misteak :-)
factor:0.20
final falue:100.00
Float Value:100.0
float has only 6 digits of accuracy and is almost never a good choice, I would suggest you use double instead. (or BigDecimal can be better in some cases)
The reason factor is 0 instead of 0.2 in your code is because
you've set the RoundingMode to be FLOOR (which means ROUND DOWN), and
your percentage variable has an implicit scale of 0 (any BigDecimals initialised from round number without specifying scale will have scale of 0)
So when you call divide you are rounding down any decimals and you are maintaining a scale of 0, and hence 0.2 is rounded down to 0.
To get the correct number, you can either
specify the scale explicitly, or
since you know you are dividing against 100, you can just use the BigDecimal#divide(BigDecimal) method instead (without providing scale or RoundingMethod). In your case, the method will not throw ArithmeticException since there is no possibility of non-terminating decimals (think 20 / 100 = 0.2, 20 / 10000 = 0.002 - decimals always terminate when dividing by 100).
However, if you're dividing against another number say 3 then you need to specify the scale because there is a possibility of non-terminating decimals (think 10 / 3 = 3.3333333333...)