One important point that is alluded to but not directly addressed is the difference between "precision" and "scale" and how they are used in the two statements. "precision" is the total number of significant digits in a number. "scale" is the number of digits to the right of the decimal point.
The MathContext constructor only accepts precision and RoundingMode as arguments, and therefore scale is never specified in the first statement.
setScale() obviously accepts scale as an argument, as well as RoundingMode, however precision is never specified in the second statement.
If you move the decimal point one place to the right, the difference will become clear:
// 1.
new BigDecimal("35.3456").round(new MathContext(4, RoundingMode.HALF_UP));
//result = 35.35
// 2.
new BigDecimal("35.3456").setScale(4, RoundingMode.HALF_UP);
// result = 35.3456
Answer from dale peters on Stack OverflowBigDecimal scale and rounding confusion
SOLVED: How to round up a BigDecimal - Products - Jaspersoft Community
Simplifying my ugly BigDecimal math
I'm probably not qualified to comment, since I'm new to Java, but why not use the double data type? Here's how I would solve the problem:
+u/CompileBot JAVA
public static double total(int quarters, int dimes, int nickels) {
double result;
double qValue = (0.25)*quarters;
double dValue = (0.10)*dimes;
double nValue = (0.05)*nickels;
result = qValue + dValue + nValue;
return result;
}
public static void main(String[] args){
System.out.printf("Total: $%.2f\n",total(4,5,5));
} More on reddit.com Quickest way to get 2 decimal places from BigDecimal?
One important point that is alluded to but not directly addressed is the difference between "precision" and "scale" and how they are used in the two statements. "precision" is the total number of significant digits in a number. "scale" is the number of digits to the right of the decimal point.
The MathContext constructor only accepts precision and RoundingMode as arguments, and therefore scale is never specified in the first statement.
setScale() obviously accepts scale as an argument, as well as RoundingMode, however precision is never specified in the second statement.
If you move the decimal point one place to the right, the difference will become clear:
// 1.
new BigDecimal("35.3456").round(new MathContext(4, RoundingMode.HALF_UP));
//result = 35.35
// 2.
new BigDecimal("35.3456").setScale(4, RoundingMode.HALF_UP);
// result = 35.3456
There is indeed a big difference, which you should keep in mind. setScale really set the scale of your number whereas round does round your number to the specified digits BUT it "starts from the leftmost digit of exact result" as mentioned within the jdk. So regarding your sample the results are the same, but try 0.0034 instead. Here's my note about that on my blog:
http://araklefeistel.blogspot.com/2011/06/javamathbigdecimal-difference-between.html
BigDecimal(55.325).setScale(2,RoundingMode.HALF_DOWN) //55.33 BigDecimal(55.355).setScale(2,RoundingMode.HALF_DOWN) //55.35
I would expect the first result to be 55.32. As per the second example it is rounded down to 55.35
I'm learning monetary calculations with BigDecimal. My method is simple. Take the number of quarters, dimes and nickels and return the monetary value (ie 4 quarters + 5 dimes + 5 nickels = $1.75). Simple enough. My method works, so no problem there, but when I look at it, the code block looks heavy. I'm sure it can be cleaned up.
public static BigDecimal total (int quarters, int dimes, int nickels) {
BigDecimal qValue = new BigDecimal ("0.25").setScale(2);
BigDecimal dValue = new BigDecimal ("0.10").setScale(2);
BigDecimal nValue = new BigDecimal ("0.05").setScale(2);
BigDecimal qTotal = qValue.multiply(new BigDecimal(quarters)).setScale(2);
BigDecimal dTotal = dValue.multiply(new BigDecimal(dimes)).setScale(2);
BigDecimal nTotal = nValue.multiply(new BigDecimal(nickels)).setScale(2);
BigDecimal total = qTotal.add( dTotal.add(nTotal)).setScale(2);
return total;
}So my question is, what would you do to simplify this? Is .setScale necessary in this example? Is it necessary for each bd? Also, I'm not quite sure how to word this, but should I not declare each bd the way I do? Could I sort of nest each bd declaration inside of total?
EDIT: Just went back into Eclipse to play around with the "nesting" idea and it just seems WAY too difficult to read. I didn't get it to work simply because I couldn't follow the logic of how it would be done.
I'm probably not qualified to comment, since I'm new to Java, but why not use the double data type? Here's how I would solve the problem:
+u/CompileBot JAVA
public static double total(int quarters, int dimes, int nickels) {
double result;
double qValue = (0.25)*quarters;
double dValue = (0.10)*dimes;
double nValue = (0.05)*nickels;
result = qValue + dValue + nValue;
return result;
}
public static void main(String[] args){
System.out.printf("Total: $%.2f\n",total(4,5,5));
}
You're right about the BigDecimal(double val) constructor not being precise enough. From the API; emphasis, mine:
public BigDecimal(double val)
░░░░Translates a double into a BigDecimal which is the exact decimal representation of the double's binary floating-point value. The scale of the returned BigDecimal is the smallest value such that (10scale × val) is an integer.
The results of this constructor can be somewhat unpredictable. One might assume that writing new BigDecimal(0.1) in Java creates a BigDecimal which is exactly equal to 0.1 (an unscaled value of 1, with a scale of 1), but it is actually equal to 0.1000000000000000055511151231257827021181583404541015625. This is because 0.1 cannot be represented exactly as a double (or, for that matter, as a binary fraction of any finite length). Thus, the value that is being passed in to the constructor is not exactly equal to 0.1, appearances notwithstanding.
The String constructor, on the other hand, is perfectly predictable: writing new BigDecimal("0.1") creates a BigDecimal which is exactly equal to 0.1, as one would expect. Therefore, it is generally recommended that the String constructor be used in preference to this one.
Also, in that Javadoc:
public BigDecimal(int val)
░░░░Translates an int into a BigDecimal. The scale of the BigDecimal is zero.
public BigDecimal multiply(BigDecimal multiplicand)
░░░░Returns a BigDecimal whose value is (this × multiplicand), and whose scale is (this.scale() + multiplicand.scale()).
To sum up, this means if you created your 'value' variables like so:
BigDecimal qValue = new BigDecimal ("0.25");
they would automatically have a 'scale' of 2 so you wouldn't need to set it. Likewise, if you kept your assignment of other variables as
BigDecimal qTotal = qValue.multiply(new BigDecimal(quarters));
qTotal would have a scale of 2 because your scales are 2 and 0, respectively;
Also, I know that this wasn't part of your question, but I would suggest changing your qValue, dValue, and nValue variables to constants like
private static final BigDecimal QUARTER_VALUE = new BigDecimal(".25");
Nothing wrong with how it is. It's probably not even better my way, but it's just a preference of mine.