[英]round BigDecimal to nearest 5 cents
我想弄清楚如何將金額向上舍入到最接近的 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% kosher - 我擔心在雙打轉換時可能會丟失精度。 然而,這是迄今為止我想出的最好的並且似乎有效。
使用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.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));
另請注意,上一個示例中存在錯誤(將 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<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);
}
這是我在 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;
}
}
在 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"));
}
根據您的編輯,另一種可能的解決方案是:
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_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;
}
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 的答案基本相同。
public static void roundUp()
{
try
{
System.out.println("Enter the currency : $");
Scanner keyboard = new Scanner(System.in);
String myint = keyboard.next();
if (!isEmptyOrBlank(myint).booleanValue())
{
BigDecimal d = new BigDecimal(myint);
System.out.println("Enter the round up factor: $");
String roundUpFactor = keyboard.next();
if (!isEmptyOrBlank(roundUpFactor).booleanValue())
{
BigDecimal scale = new BigDecimal(roundUpFactor);
BigDecimal y = d.divide(scale, MathContext.DECIMAL128);
BigDecimal q = y.setScale(0, 0);
BigDecimal z = q.multiply(scale);
System.out.println("Final price after rounding up to " + roundUpFactor + " is : $" + z);
System.out.println("Want to try with other price Y/N :");
String exit = keyboard.next();
if ((!isEmptyOrBlank(exit).booleanValue()) && ("y".equalsIgnoreCase(exit))) {
roundUp();
} else {
System.out.println("See you take care");
}
}
}
else
{
System.out.println("Please be serious u r dealing with critical Tx Pricing");
}
}
catch (Exception e)
{
System.out.println("Please be serious u r dealing with critical Tx Pricing enter correct rounding off value");
}
}
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));
你可以使用普通的 double 來做到這一點。
double amount = 990.49;
double rounded = ((double) (long) (amount * 20 + 0.5)) / 20;
編輯:對於負數,您需要減去 0.5
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.