BigDecimal adding wrong value
Use a String literal:
private static final BigDecimal sd = new BigDecimal("0.7");
If you use a double
, actually public BigDecimal(double val)
is called. The reason you do not get 0.7 is that it cannot be exactly represented by a double
. See the linked JavaDoc for more information.
BigDecimal divide returning wrong result for some calculations
The problem is that you're using the wrong BigDecimal#divide
for your rounding to 2 decimals. Here are the available arguments for the BigDecimal#divide
methods:
divide(BigDecimal divisor)
divide(BigDecimal divisor, int roundingMode)
divide(BigDecimal divisor, MathContext mc)
divide(BigDecimal divisor, RoundingMode roundingMode)
divide(BigDecimal divisor, int scale, int roundingMode)
divide(BigDecimal divisor, int scale, RoundingMode roundingMode)
Since you're using a divide
with a BigDecimal
and int
argument, it therefore uses the divide(BigDecimal divisor, int roundingMode)
, where your 2
is the rounding mode and NOT the scale. In this case, 2
is actually ROUND_CEILING
, and scale is unspecified.
Instead, you'll have to use either the divide(BigDecimal divisor, int scale, int roundingMode)
or divide(BigDecimal divisor, int scale, RoundingMode roundingMode)
. So change your calls to:
System.out.println(BigDecimal.valueOf(2.8).divide(BigDecimal.valueOf(3.87), 2,
BigDecimal.ROUND_HALF_UP));
System.out.println(BigDecimal.valueOf(2.41).divide(BigDecimal.valueOf(2.73), 2,
BigDecimal.ROUND_HALF_UP));
(Feel free to use a rounding mode other than ROUND_HALF_UP
.)
Try it online.
I'm not sure what scale it uses by default, but the ROUND_CEILING
you've specified with your 2
caused the issues in your calculations.
As for the mentioned comments, there are three possible ways to create the BigDecimal
with the specified values:
BigDecimal.valueOf(2.8)
new BigDecimal(2.8)
new BigDecimal("2.8")
The new BigDecimal(2.8)
still gives floating point errors, so I would advice to use either BigDecimal.valueOf(2.8)
as you already did, or the String constructor new BigDecimal("2.8")
.
Try it online.
This is all a bit irrelevant for your rounding issues however, since using the correct divide
method will give the correct results regardless of the BigDecimal
initialization you've used:
Try it online.
BigDecimal from Double incorrect value?
When you create a double, the value 0.3 cannot be represented exactly. You can create a BigDecimal from a string without the intermediate double, as in
new BigDecimal("0.3")
A floating point number is represented as a binary fraction and an exponent. Therefore there are some number that cannot be represented exactly. There is an analogous problem in base 10 with numbers like 1/3, which is 0.333333333..... Any decimal representation of 1/3 is inexact. This happens to a DIFFERENT set of fractions in binary, and 0.3 is one of the set that is inexact in binary.
BigDecimal.Divide gives incorrect result
You asked the divide()
call for 13 digits of precision, and that is what it gave you.
You then convert that to double
and notice that double
cannot handle that precision.
BigDecimal d1 = new BigDecimal(String.valueOf(221500.0)).setScale(13, BigDecimal.ROUND_HALF_UP);
BigDecimal d2 = new BigDecimal(String.valueOf(12.0)).setScale(13, BigDecimal.ROUND_HALF_UP);
BigDecimal d3 = d1.divide(d2, 13, BigDecimal.ROUND_HALF_UP);
System.out.println("BigDecimal: " + d3);
System.out.println("as double : " + d3.doubleValue());
OUTPUT
BigDecimal: 18458.3333333333333
as double : 18458.333333333332
If you're going to throw away the extra precision gained by using BigDecimal
, why not just do the math in double
?
On a different note:
Don't do new BigDecimal(String.valueOf(doubleValue))
.
Use BigDecimal.valueOf(doubleValue)
.
Setting the scale of d1
and d2
seems rather meaningless.
Unless explicitly needed, round as late as possible, if at all.
Multiplying two numbers using BigDecimal returns a wrong value
You're not multiplying two numbers using BigDecimal
. You're multiplying them using double
arithmetic, and passing the result to the BigDecimal
constructor.
You want:
new BigDecimal("0.06").multiply(new BigDecimal("3")).toString()
Note that you do want the values in strings - otherwise you're using the double
value for 0.06, which isn't exactly 0.06... you've lost information before you start. (You don't really need the string form of 3, but I've done so for consistency.)
For example:
System.out.println(new BigDecimal(0.06));
prints
0.059999999999999997779553950749686919152736663818359375
BigDecimal Error
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));
}
Java BigDecimal operations arbitrarily showing incorrect number of decimal places
The problem is probably a misunderstanding of MathContext
. With MathContext(2, RoundingMode.UP)
you're not saying "I want 2 decimal places", but you say "I want to reduce the precision of my number to 2 digits".
If you have a look at your numbers, the scale (number of decimal places) is 2 where hours - lim >= 10
If you have a detailed look at the two examples hours=12.5
and hours=13.0
//hours = 12.5:
var subtract = hours.subtract("3.00"); // 12.5 - 3.00 = 9.50 (subtract.scale = 2)
var round = subtract.round(mc); // 9.5 (round.scale = 1)
var result = round.multiply("0.50") // 9.5 * 0.50 = 4.750 (result.scale = round.scale + subtract.scale = 3)
//hours = 13.0:
var subtract = hours.subtract("3.00"); // 13.0 - 3.00 = 10.00 (subtract.scale = 2)
var round = subtract.round(mc); // 10 (round.scale = 0)
var result = round.multiply("0.50") // 10 * 0.50 = 5.00 (result.scale = round.scale + subtract.scale = 2)
You see the scale of the rounded decimal becomes 0 as soon as it's value becomes >= 10. The scale of the result of multiply is always the sum of the scale of the two multiplicants.
To set the scale of a BigDecimal
use BigDecimal.setScale(int, RoundingMode)
instead of MathContext
.
Related Topics
Differencebetween Reader and Inputstream
How to Clear Permgen Space Error in Tomcat
What Are the Date Formats Available in Simpledateformat Class
Turning an Executorservice to Daemon in Java
How to Ignore Pkix Path Building Failed: Sun.Security.Provider.Certpath.Suncertpathbuilderexception
Why Should I Override Hashcode() When I Override Equals() Method
Spring Boot Actuator Without Spring Boot
Apache Httpclient Making Multipart Form Post
Why Are Wait() and Notify() Declared in Java's Object Class
Rejectedexecutionexception Inside Single Executor Service
String S = New String("Xyz"). How Many Objects Has Been Made After This Line of Code Execute
Changing Java Platform on Which Netbeans Runs
How to Run MySQL In-Memory for Junit Test Cases
"Content Is Not Allowed in Prolog" When Parsing Perfectly Valid Xml on Gae
Netbeans - Error: Could Not Find or Load Main Class
Is There a Java Equivalent of the Python Eval Function
How to Use This Boolean in an If Statement
How to Run All Tests Belonging to a Certain Category in Junit 4