将BigDecimal舍入到最接近的5美分

我想弄清楚如何将货币金额向上舍入到最接近的5美分。 以下显示了我的预期结果

1.03 => 1.05 1.051 => 1.10 1.05 => 1.05 1.900001 => 1.10 

我需要结果的精度为2(如上所示)。

更新

按照下面的建议,我能做的最好的就是这个

  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) 

我不相信这是100%犹太人 – 我担心转换到双打时精度可能会丢失。 然而,这是迄今为止我提出的最好的, 似乎有效。

您可以使用plain double来执行此操作。

 double amount = 990.49; double rounded = ((double) (long) (amount * 20 + 0.5)) / 20; 

编辑:对于负数,您需要减去0.5

使用没有任何双打的BigDecimal (改进了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; } } 

舍入模式例如是RoundingMode.HALF_UP 。 对于您的示例,您实际上需要RoundingMode.UPbd是一个只返回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)); 

另请注意,您的上一个示例中存在错误(将1.900001舍入到1.10)。

我会尝试乘以20,四舍五入到最接近的整数,然后除以20.这是一个黑客,但应该得到你正确的答案。

我几年前在Java中写过: https : //github.com/marcolopes/dma/blob/master/org.dma.java/src/org/dma/java/math/BusinessRules.java

 /** * Rounds the number to the nearest
* Numbers can be with or without decimals
*/ 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
* Numbers can be with or without decimals
* Example: 5, 10 = 10 *

* HALF_UP
* 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
* Numbers can be with or without decimals
* Example: 5, 10 = 0 *

* HALF_DOWN
* 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); }

这里有一些非常简单的方法,我写的c#总是向上或向下舍入到任何传递的值。

 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; } } 

根据您的编辑,另一个可能的解决方案是:

 BigDecimal twenty = new BigDecimal(20); BigDecimal amount = new BigDecimal(990.49) // To round to the nearest .05, multiply by 20, round to the nearest integer, then divide by 20 BigDecimal result = new BigDecimal(amount.multiply(twenty) .add(new BigDecimal("0.5")) .toBigInteger()).divide(twenty); 

这样做的好处是保证不会失去精确度,当然它可能会慢一点……

和scala测试日志:

 scala> var twenty = new java.math.BigDecimal(20) twenty: java.math.BigDecimal = 20 scala> var amount = new java.math.BigDecimal("990.49"); amount: java.math.BigDecimal = 990.49 scala> new BigDecimal(amount.multiply(twenty).add(new BigDecimal("0.5")).toBigInteger()).divide(twenty) res31: java.math.BigDecimal = 990.5 

要通过此测试:

 assertEquals(bd("1.00"), round(bd("1.00"))); assertEquals(bd("1.00"), round(bd("1.01"))); assertEquals(bd("1.00"), round(bd("1.02"))); assertEquals(bd("1.00"), round(bd("1.024"))); assertEquals(bd("1.05"), round(bd("1.025"))); assertEquals(bd("1.05"), round(bd("1.026"))); assertEquals(bd("1.05"), round(bd("1.049"))); assertEquals(bd("-1.00"), round(bd("-1.00"))); assertEquals(bd("-1.00"), round(bd("-1.01"))); assertEquals(bd("-1.00"), round(bd("-1.02"))); assertEquals(bd("-1.00"), round(bd("-1.024"))); assertEquals(bd("-1.00"), round(bd("-1.0245"))); assertEquals(bd("-1.05"), round(bd("-1.025"))); assertEquals(bd("-1.05"), round(bd("-1.026"))); assertEquals(bd("-1.05"), round(bd("-1.049"))); 

ROUND_HALF_UP更改ROUND_HALF_UP

 private static final BigDecimal INCREMENT_INVERTED = new BigDecimal("20"); public BigDecimal round(BigDecimal toRound) { BigDecimal divided = toRound.multiply(INCREMENT_INVERTED) .setScale(0, BigDecimal.ROUND_HALF_UP); BigDecimal result = divided.divide(INCREMENT_INVERTED) .setScale(2, BigDecimal.ROUND_HALF_UP); return result; } 

在Scala中我做了以下(Java下面)

 import scala.math.BigDecimal.RoundingMode def toFive( v: BigDecimal, digits: Int, roundType: RoundingMode.Value= RoundingMode.HALF_UP ):BigDecimal = BigDecimal((2*v).setScale(digits-1, roundType).toString)/2 

而在Java中

 import java.math.BigDecimal; import java.math.RoundingMode; public static BigDecimal toFive(BigDecimal v){ return new BigDecimal("2").multiply(v).setScale(1, RoundingMode.HALF_UP).divide(new BigDecimal("2")); } 
  public static BigDecimal roundTo5Cents(BigDecimal amount) { amount = amount.multiply(new BigDecimal("2")); amount = amount.setScale(1, RoundingMode.HALF_UP); // preferred scale after rounding to 5 cents: 2 decimal places amount = amount.divide(new BigDecimal("2"), 2, RoundingMode.HALF_UP); return amount; } 

请注意,这与John的答案基本相同。

Tom有正确的想法,但您需要使用BigDecimal方法,因为您表面上使用的是BigDecimal,因为您的值不适合原始数据类型。 就像是:

 BigDecimal num = new BigDecimal(0.23); BigDecimal twenty = new BigDecimal(20); //Might want to use RoundingMode.UP instead, //depending on desired behavior for negative values of num. BigDecimal numTimesTwenty = num.multiply(twenty, new MathContext(0, RoundingMode.CEILING)); BigDecimal numRoundedUpToNearestFiveCents = numTimesTwenty.divide(twenty, new MathContext(2, RoundingMode.UNNECESSARY));