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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2106615/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 03:36:25  来源:igfitidea点击:

round BigDecimal to nearest 5 cents

javaroundingbigdecimal

提问by Dónal

I'm trying to figure out how to round a monetary amount upwards to the nearest 5 cents. The following shows my expected results

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

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).

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

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 seemsto work.

我不相信这是 100% kosher - 我担心在双打转换时可能会丢失精度。然而,这是迄今为止我想出的最好的并且似乎有效。

采纳答案by Peter Lawrey

You can use plain double to do this.

你可以使用普通的 double 来做到这一点。

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

EDIT: for negative numbers you need to subtract 0.5

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

回答by James Cronen

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.

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

回答by Matt J

Tom has the right idea, but you need to use BigDecimal methods, since you ostensibly are using BigDecimal because your values are not amenable to a primitive datatype. Something like:

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

回答by Paul Wagland

Based on your edit, another possible solution would be:

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

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

This has the advantage, of being guaranteed not to lose precision, although it could potentially be slower of course...

这有一个优点,可以保证不会失去精度,尽管它当然可能会更慢......

And the scala test log:

和 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

回答by NER1808

Here are a couple of very simple methods in c# I wrote to always round up or down to any value passed.

这是我在 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;
        }
    }

回答by marcolopes

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

几年前我用 Java 写了这个: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);

}

回答by robinst

Using BigDecimalwithout any doubles (improved on the answer from marcolopes):

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

The rounding mode is e.g. RoundingMode.HALF_UP. For your examples, you actually want RoundingMode.UP(bdis a helper which just returns new BigDecimal(input)):

舍入模式是例如RoundingMode.HALF_UP。对于您的示例,您实际上想要RoundingMode.UP(bd是一个刚返回的助手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).

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

回答by Micka?l Gauvin

For this test to pass :

要通过此测试:

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")));

Change ROUND_UPin ROUND_HALF_UP:

更改ROUND_UPROUND_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;
}

回答by John

In Scala I did the following (Java below)

在 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

And in Java

在 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"));
}

回答by Reto H?hener

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

Note that this is basically the same answer as John's.

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