BigDecimal 除以奇数小数

BigDecimal Dividing with uneven decimals

我正在尝试编写一个程序来处理多个必需的 BigDecimal 计算。我很困惑,因为 BigDecimal 没有按照我的意愿进行合作。让我解释一下我需要它做什么:

首先它需要一个最多可以有两位小数的货币金额。然后它需要 "allocations" ,这基本上是该金额将分布在多少个帐户中。

程序现在应该在账户之间分配金额。当然在某些情况下金额不能平分,例如 3.33 美元分给 2 个账户。在这种情况下,您必须有 1 个分配额外的美分或将数字四舍五入。四舍五入不是一种选择,每一分钱都必须考虑在内。这是我目前所拥有的:

totalAllocations = TransactionWizard.totalAllocations;//Set in another class, how many accounts total will be spread

    BigDecimal totalAllocationsBD = new BigDecimal(totalAllocations).setScale(2);//Converts to big decimal. 

    amountTotal = (BigDecimal) transInfo.get("amount"); // set total amount

    MathContext mc = new MathContext(2);
    remainderAllocation = amountTotal.remainder(totalAllocationsBD, mc);

    dividedAllocationAmount = amountTotal.divide(totalAllocationsBD, MathContext.DECIMAL32);

    dividedAllocationAmount=dividedAllocationAmount.setScale(2);

稍后在 class 中我实际上写了值。我首先有一个计数器,它被设置为 totalAllocations。然后我有一个循环,它将写入一些信息,包括 dividedAllocationAmount。所以说 amountTotal 是 10,我有两个分配,然后 5.00 将被写入两次。

我想要的是在总分配不能平均分配的情况下,有一个额外的分配来容纳剩余部分,如下所示:

if(remainderAllocation.compareTo(BigDecimal.ZERO) >0 && allocationCounter==1){
                adjAmt.setValue(remainderAllocation);
            }else{
                adjAmt.setValue(dividedAllocationAmount);
            }

adjAmt 只是设置一个 XML 字段,这是一个 JAXB 项目。

我这里遇到的主要问题是有余数的数字。例如,如果用户选择 2 个分配且金额为 3.33 美元,则程序将失败并给我一个舍入错误。

Exception in thread "AWT-EventQueue-0" java.lang.ArithmeticException: Rounding necessary
    at java.math.BigDecimal.commonNeedIncrement(Unknown Source)
    at java.math.BigDecimal.needIncrement(Unknown Source)
    at java.math.BigDecimal.divideAndRound(Unknown Source)
    at java.math.BigDecimal.setScale(Unknown Source)
    at java.math.BigDecimal.setScale(Unknown Source)
    at model.Creator.createTransaction(Creator.java:341)
    at view.TransactionWizard.actionPerformed(TransactionWizard.java:333)
    at javax.swing.AbstractButton.fireActionPerformed(Unknown Source)
    at javax.swing.AbstractButton$Handler.actionPerformed(Unknown Source)
    at javax.swing.DefaultButtonModel.fireActionPerformed(Unknown Source)
    at javax.swing.DefaultButtonModel.setPressed(Unknown Source)
    at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(Unknown Source)
    at java.awt.Component.processMouseEvent(Unknown Source)
    at javax.swing.JComponent.processMouseEvent(Unknown Source)
    at java.awt.Component.processEvent(Unknown Source)
    at java.awt.Container.processEvent(Unknown Source)
    at java.awt.Component.dispatchEventImpl(Unknown Source)
    at java.awt.Container.dispatchEventImpl(Unknown Source)
    at java.awt.Component.dispatchEvent(Unknown Source)
    at java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)
    at java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)
    at java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)
    at java.awt.Container.dispatchEventImpl(Unknown Source)
    at java.awt.Window.dispatchEventImpl(Unknown Source)
    at java.awt.Component.dispatchEvent(Unknown Source)
    at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
    at java.awt.EventQueue.access0(Unknown Source)
    at java.awt.EventQueue.run(Unknown Source)
    at java.awt.EventQueue.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
    at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
    at java.awt.EventQueue.run(Unknown Source)
    at java.awt.EventQueue.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
    at java.awt.EventQueue.dispatchEvent(Unknown Source)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
    at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
    at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
    at java.awt.WaitDispatchSupport.run(Unknown Source)
    at java.awt.WaitDispatchSupport.run(Unknown Source)
    at java.awt.WaitDispatchSupport.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.awt.WaitDispatchSupport.enter(Unknown Source)
    at java.awt.Dialog.show(Unknown Source)
    at java.awt.Component.show(Unknown Source)
    at java.awt.Component.setVisible(Unknown Source)
    at java.awt.Window.setVisible(Unknown Source)
    at java.awt.Dialog.setVisible(Unknown Source)
    at view.MainView.run(MainView.java:398)
    at java.awt.event.InvocationEvent.dispatch(Unknown Source)
    at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
    at java.awt.EventQueue.access0(Unknown Source)
    at java.awt.EventQueue.run(Unknown Source)
    at java.awt.EventQueue.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
    at java.awt.EventQueue.dispatchEvent(Unknown Source)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
    at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
    at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
    at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
    at java.awt.EventDispatchThread.run(Unknown Source)
Local Instrument: WEB

如果总金额为 10.51 美元并且有 3 次分配,也会发生同样的情况。实际上,我想要的是两次分配为 5.25 美元,第三次分配为 0.01 美元。 (因为那是其余部分)。我该怎么办?

通过自己的反复试验,我终于找到了解决这个问题的方法。事实证明,对我来说关键是先取出余数,然后再进行除法。

因为我知道我想根据情况自动将大小数分成三部分,所以我这样做了..

package mathtest;

import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;

public class MathTest 

{

    public static BigDecimal totalAmount;
    public static BigDecimal totalAllocations; 
    public static BigDecimal divisibleAmount;
    public static BigDecimal remainderAmount;
    public static BigDecimal subtractDecimal;


    public static void main(String args[]){

        MathContext mc = new MathContext(2);

        totalAmount = new BigDecimal(10.00).setScale(2,RoundingMode.HALF_UP);//Sets the total monetary amount. Standard monetary rounding. 
        totalAllocations = new BigDecimal(2);//The number of accounts the total amount will be split between. 
        subtractDecimal = new BigDecimal(1.00);//Used to remove one from the total allocations (to account for the remainder). 

        remainderAmount = totalAmount.remainder(totalAllocations, mc);//Gets the remainder assuming you tried to divide total/allocations. 

        totalAmount=totalAmount.subtract(remainderAmount);//Subtracts the remainder from the total. 


        if(remainderAmount.compareTo(BigDecimal.ZERO) >0){//If there is a remainder. 

        //The divisible amount is the total amount divided by the total allocations minus 1 (to account for remainder). 
        divisibleAmount=totalAmount.divide(totalAllocations.subtract(subtractDecimal));

        }else{//If there is no remainder 

            divisibleAmount=totalAmount.divide(totalAllocations);//The divisible amount is the total amount divided by the total allocations. 
        }
        if(remainderAmount.compareTo(BigDecimal.ZERO)>0){
        System.out.println(remainderAmount);    
        }


        //The below would be printed once for each allocation. 

        System.out.println(divisibleAmount);
    }

}

Of course in some situations the amount can not be evenly divided, for example .33 divided between 2 accounts. In that case you must either have 1 allocation with a extra cent or round the number

[无优化、最佳实践、错误处理、数据类型转换等。普通工作伪代码。]

试试这个

        float amount = 3.33f;

        int allocations = 2;

        double average = amount/allocations;

        System.out.println("ACTUAL AVERAGE "+average);

        double rounded = Math.round(average * 100.0) / 100.0;

        System.out.println("ROUNDED VALUE: "+rounded);

        double adjustment = average - rounded;

        adjustment*=allocations; //-- FOR EACH ALLOCATION

        for(int i=1; i<allocations; i++){
            System.out.println("Allocation :" +i + " = "+rounded);
        }

        //-- ADDING ADJUSTED ROUNDING AMOUNT TO LAST ONE
        double adjustedAmount = Math.round((rounded+adjustment) * 100.0) / 100.0;

        System.out.println("Allocation :" +allocations +" = " + adjustedAmount);

2 次分配的金额 3.33 的输出。

ACTUAL AVERAGE 1.6649999618530273
ROUNDED VALUE: 1.66
Allocation :1 = 1.66
Allocation :2 = 1.67 //-- EXTRA CENT

The same happens if the total amount is .51 and there are 3 allocations. In reality what I want is for two allocations to be for .25 and the 3rd allocation to be for [=13=].01. (Since that is the remainder). What do I do?

现在这个和上面说的不一样了,你可以有3.5, 3.5 & 3.51.

但是如果你想要单独的 0.01,那么改变上面的代码 allocations-1 并将余数设置为最后一次分配。因此,第 2 次分配为 5.25,第 3 次分配为余数 0.01,希望对您有所帮助。