I am trying to work on a program for work with multiple BigDecimal calculations necessary. I am at quite a confusing point because the BigDecimal is not cooperating how I would like. Let me explain what I need it to do:
First it takes a monetary amount which can have up to two decimal places. Then it takes the "allocations" which is basically how many accounts that amount will be spread up through.
The program should now divide the amount between the accounts. Of course in some situations the amount can not be evenly divided, for example $3.33 divided between 2 accounts. In that case you must either have 1 allocation with a extra cent or round the number. Rounding is not an option, every penny must be accounted for. Here is what I have so far:
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);
Later in the class I actually write the values. I have a counter first that is set as the totalAllocations. I then have a loop that will write a bit of information including the dividedAllocationAmount. So say the amountTotal had been 10 and I had two allocations then 5.00 would be written twice.
What I want is for situations where the total can't be evenly divided among the allocations for there to be one extra allocation to hold the remainder as shown below:
if(remainderAllocation.compareTo(BigDecimal.ZERO) >0 && allocationCounter==1){
adjAmt.setValue(remainderAllocation);
}else{
adjAmt.setValue(dividedAllocationAmount);
}
The adjAmt is just setting an XML field, this is a JAXB project.
The main issue I have here is with numbers with a remainder. For example if a user selects 2 allocations and the amount is $3.33 then the program will fail and give me a rounding error.
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$2.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.access$500(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.awt.EventQueue$3.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$4.run(Unknown Source)
at java.awt.EventQueue$4.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$2.run(Unknown Source)
at java.awt.WaitDispatchSupport$4.run(Unknown Source)
at java.awt.WaitDispatchSupport$4.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$15$1.run(MainView.java:398)
at java.awt.event.InvocationEvent.dispatch(Unknown Source)
at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
at java.awt.EventQueue.access$500(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.awt.EventQueue$3.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
The same happens if the total amount is $10.51 and there are 3 allocations. In reality what I want is for two allocations to be for $5.25 and the 3rd allocation to be for $0.01. (Since that is the remainder). What do I do?
Of course in some situations the amount can not be evenly divided, for example $3.33 divided between 2 accounts. In that case you must either have 1 allocation with a extra cent or round the number
[No optimization, best practices, error handling, data type conversion etc. plain working pseudo code.]
Try this
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);
Output for amount 3.33 with 2 allocations.
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 $10.51 and there are 3 allocations. In reality what I want is for two allocations to be for $5.25 and the 3rd allocation to be for $0.01. (Since that is the remainder). What do I do?
Now this is different from above what you said above, you can have 3.5, 3.5 & 3.51.
But if you want 0.01 separately, then alter above code allocations-1 & set the remainder to the last allocation. So it would be 5.25 for 2 & remainder 0.01 for the 3rd allocation, hope this helps.