round BigDecimal to nearest 5 cents
Solution 1
You can use plain double to do this.
double amount = 990.49;
double rounded = ((double) (long) (amount * 20 + 0.5)) / 20;
EDIT: for negative numbers you need to subtract 0.5
Solution 2
Using BigDecimal
without any doubles (improved on the answer from marcolopes):
public static BigDecimal round(BigDecimal value, BigDecimal increment,
RoundingMode roundingMode) {
if (increment.signum() == 0) {
// 0 increment does not make much sense, but prevent division by 0
return value;
} else {
BigDecimal divided = value.divide(increment, 0, roundingMode);
BigDecimal result = divided.multiply(increment);
return result;
}
}
The rounding mode is e.g. RoundingMode.HALF_UP
. For your examples, you actually want RoundingMode.UP
(bd
is a helper which just returns new BigDecimal(input)
):
assertEquals(bd("1.05"), round(bd("1.03"), bd("0.05"), RoundingMode.UP));
assertEquals(bd("1.10"), round(bd("1.051"), bd("0.05"), RoundingMode.UP));
assertEquals(bd("1.05"), round(bd("1.05"), bd("0.05"), RoundingMode.UP));
assertEquals(bd("1.95"), round(bd("1.900001"), bd("0.05"), RoundingMode.UP));
Also note that there is a mistake in your last example (rounding 1.900001 to 1.10).
Solution 3
I'd try multiplying by 20, rounding to the nearest integer, then dividing by 20. It's a hack, but should get you the right answer.
Solution 4
I wrote this in Java a few years ago: https://github.com/marcolopes/dma/blob/master/org.dma.java/src/org/dma/java/math/BusinessRules.java
/**
* Rounds the number to the nearest<br>
* Numbers can be with or without decimals<br>
*/
public static BigDecimal round(BigDecimal value, BigDecimal rounding, RoundingMode roundingMode){
return rounding.signum()==0 ? value :
(value.divide(rounding,0,roundingMode)).multiply(rounding);
}
/**
* Rounds the number to the nearest<br>
* Numbers can be with or without decimals<br>
* Example: 5, 10 = 10
*<p>
* HALF_UP<br>
* Rounding mode to round towards "nearest neighbor" unless
* both neighbors are equidistant, in which case round up.
* Behaves as for RoundingMode.UP if the discarded fraction is >= 0.5;
* otherwise, behaves as for RoundingMode.DOWN.
* Note that this is the rounding mode commonly taught at school.
*/
public static BigDecimal roundUp(BigDecimal value, BigDecimal rounding){
return round(value, rounding, RoundingMode.HALF_UP);
}
/**
* Rounds the number to the nearest<br>
* Numbers can be with or without decimals<br>
* Example: 5, 10 = 0
*<p>
* HALF_DOWN<br>
* Rounding mode to round towards "nearest neighbor" unless
* both neighbors are equidistant, in which case round down.
* Behaves as for RoundingMode.UP if the discarded fraction is > 0.5;
* otherwise, behaves as for RoundingMode.DOWN.
*/
public static BigDecimal roundDown(BigDecimal value, BigDecimal rounding){
return round(value, rounding, RoundingMode.HALF_DOWN);
}
Solution 5
Here are a couple of very simple methods in c# I wrote to always round up or down to any value passed.
public static Double RoundUpToNearest(Double passednumber, Double roundto)
{
// 105.5 up to nearest 1 = 106
// 105.5 up to nearest 10 = 110
// 105.5 up to nearest 7 = 112
// 105.5 up to nearest 100 = 200
// 105.5 up to nearest 0.2 = 105.6
// 105.5 up to nearest 0.3 = 105.6
//if no rounto then just pass original number back
if (roundto == 0)
{
return passednumber;
}
else
{
return Math.Ceiling(passednumber / roundto) * roundto;
}
}
public static Double RoundDownToNearest(Double passednumber, Double roundto)
{
// 105.5 down to nearest 1 = 105
// 105.5 down to nearest 10 = 100
// 105.5 down to nearest 7 = 105
// 105.5 down to nearest 100 = 100
// 105.5 down to nearest 0.2 = 105.4
// 105.5 down to nearest 0.3 = 105.3
//if no rounto then just pass original number back
if (roundto == 0)
{
return passednumber;
}
else
{
return Math.Floor(passednumber / roundto) * roundto;
}
}
Dónal
I earn a living by editing text files. I can be contacted at: [email protected] You can find out about all the different kinds of text files I've edited at: My StackOverflow Careers profile
Updated on September 05, 2022Comments
-
Dónal over 1 year
I'm trying to figure out how to round a monetary amount upwards to the nearest 5 cents. The following shows my expected results
1.03 => 1.05 1.051 => 1.10 1.05 => 1.05 1.900001 => 1.10
I need the result to be have a precision of 2 (as shown above).
Update
Following the advice below, the best I could do is this
BigDecimal amount = new BigDecimal(990.49) // To round to the nearest .05, multiply by 20, round to the nearest integer, then divide by 20 def result = new BigDecimal(Math.ceil(amount.doubleValue() * 20) / 20) result.setScale(2, RoundingMode.HALF_UP)
I'm not convinced this is 100% kosher - I'm concerned precision could be lost when converting to and from doubles. However, it's the best I've come up with so far and seems to work.
-
Dónal over 14 yearsThis throws an exception Exception thrown: Rounding necessary java.lang.ArithmeticException: Rounding necessary
-
robinst almost 11 yearsI think using
rounding.signum() == 0
would be a better test for 0 instead ofrounding.doubleValue() == 0
. Apart from that, this solution is good. -
daemonl almost 11 yearsdocs.oracle.com/javase/tutorial/java/nutsandbolts/… "As mentioned above, this data type [double] should never be used for precise values, such as currency."
-
Vishy almost 11 years@daemonl A good quote, except that most trading systems use double or long, especially those using C++.
-
Rajanik Jarasania almost 10 yearsClearly the best answer. Please upvote so it's ranked higher than those hacky solutions that use floating point.
-
D. Kovács about 7 years@PeterLawrey source for this statement? I have never ever seen such a system, but I've worked with PL/I, C, and C++ systems in banks and other institutions...
-
Vishy about 7 years@D.Kovács which data type did they use? I have consulted for more than half the big banks in London and New York on these systems.
-
D. Kovács about 7 years@PeterLawrey The systems I have encountered always used self-built data structures for storing monetary data. I don't say this as an absolute, just stating that using plain
double
is a very, very, VERY bad idea. -
Vishy about 7 years@D.Kovács I can understand developers not being comfortable with using
double
and it has it's quirks but in investment banks, they rarely use anything else. -
Vishy about 7 years@DavidEasley I can understand developers not feeling comfortable using language primitives and operations like double, but that doesn't make using them hacky.
-
D. Kovács about 7 years@PeterLawrey interesting, I currently work in the insurance sector with Java, and everything is in BigDecimal...
-
Vishy about 7 years@D.Kovács I have worked on systems which are all BigDecimal. My first year of consulting was basically paid for by replacing BigDecimal with double for various clients, so I owe a lot to it ;)
-
Rajanik Jarasania about 7 years@PeterLawrey Genuinely interested to know how you did this without introducing rounding errors. Why are Oracle wrong to say double should never be used for precise values, such as currency?
-
Vishy about 7 years@DavidEasley there is a myth that you don't need to worry about rounding. It is rare that your operations are always so simple you never need to worry about rounding. Even if you use BigDecimal you need to get rounding right. The "nice" thing about double is it us often obviously wrong e.g 0.9999999999999998 but BigDecimal might be 0.99. Is it right or wrong?
-
Vishy about 7 years@DavidEasley in the case above, when you have two values which are represented without error and you perform an operation you get the nearest representable value to the result. As long as you are not working to the limits of the precision of double this isn't a problem.
-
Rajanik Jarasania about 7 years@PeterLawrey Thanks for responding but you're still giving us rather vague, handwavy answers. There are many references on the net to the danger of using floating point for things like currency, i.e. where precision is critical. Please can you point to just one reference (ideally a reputable one) that backs up your assertion that floating point (or at least double) is fine for financial calculations.
-
Vishy about 7 years@DavidEasley I would point to the IEEE-754 spec which explains exactly how it works in a consistent and predictable manner. What you consider reputable is purely a matter of opinion, I can tell you that the reality is that most investment banks use
double
in Java, and you can chose not to believe me if you like. Projects using BigDecimal have made me a lot of consulting money over the years, so please keep using it if you prefer it. -
D. Kovács about 7 years@PeterLawrey, so that's why invesment banking has more problem than it should.
-
Vishy about 7 years@D.Kovács when a bank loses $84 billion in a quarter it wasn't a rounding error :P I was at bank which did this.
-
D. Kovács almost 7 years@PeterLawrey "I was" - and we know why the past tense ;) Joking apart: no that was a very elaborate hack. If we are talking about the same event. But in the case I have in my mind, it wasn't one bank but several. So maybe it's another :)
-
Tuupertunut over 6 yearsThey are not literally hacky but they only work on BigDecimals small enough to fit into double. Therefore they are not reliable solutions. This one works on any BigDecimal.
-
marcolopes over 6 yearsThanks! Thats correct... this code is old and i was new to java! :D signum() is the way to go!
-
user207421 over 6 yearsNot a hack at all, a perfectly sound technique.
-
AbuNassar over 6 yearsUsing BigDecimal is much more expressive of the fact that you're manipulating currency amounts.
-
Vishal Patel over 3 yearsthis one is the perfect answer and solves my problem.
-
Admin over 3 yearsThis pseudo takes inputs from line command where you can enter the value to round up along with round up value i.e. Enter the currency : $ 1.051 Enter the round up factor: $ 0.05 Final price after rounding up to 0.05 is : $1.10