Forum Moderators: coopster

Message Too Old, No Replies

Code to divide money without rounding errors

         

aspdaddy

12:10 pm on Jul 30, 2010 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Does anyone here know the code to divide a given amount of money between a given amount of people such that the sums given to each person add up to exactly the total.

It does not matter if the amounts are not identical but should minimise the differences

If possible it should be able to predict the amounts rather than work by testing them after the divide has been done

Thanks

coopster

1:18 pm on Jul 30, 2010 (gmt 0)

WebmasterWorld Administrator 10+ Year Member



  1. var a = divide the total by the number of people, rounding it to two decimal places.
  2. var b = multiply that number (a) by the number of people.
  3. Compare b to total, if it is more, reduce the last person's amount by the difference. If less, increase the last person's amount by the difference.

aspdaddy

2:22 pm on Jul 30, 2010 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Thanks, something like that I have already. Doesnt minimise differences or predict parts in advance though.



Dim intCounter As Integer
Dim tmpPart As Integer
Dim intDifference As Integer
Dim tmpAnswer As String
Dim intNumberOfParts As Integer
Dim intAmount

' grab inputs from form
intNumberOfParts = CInt(txtQuals.Text)

' multiply amount by 100 to help working in money format
intAmount = CDbl(txtAmount.Text) * 100

' create array for results
Dim arrParts() As Integer
ReDim arrParts(intNumberOfParts)

' set rough value always rounding down
tmpPart = Int(CInt(intAmount) / intNumberOfParts)

' copy rough value to all parts
For intCounter = 0 To UBound(arrParts, 1) - 1
arrParts(intCounter) = tmpPart
Next

' calc difference that rounding caused
intDifference = CInt(intAmount) - (tmpPart * intNumberOfParts)

' add difference to first part
arrParts(0) = arrParts(0) + intDifference

coopster

5:07 pm on Jul 30, 2010 (gmt 0)

WebmasterWorld Administrator 10+ Year Member



Parts are going to be pennies. How many? I suppose you could use modulus to determine that. Or merely loop over the remainder of the over/under and add/subtract to each entry as you allot to each person. Once the remainder is at zero you quit adding/subtracting from the over/under and allocate the rest at equal shares. Everybody would be within a penny that way.

aspdaddy

9:01 am on Aug 16, 2010 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Thought I had it nailed, but have one bug.Any ideas why this evaluates to 1639 in Windows, when ohers like 32.60 & 32.40 behave themselves..

Int(cdbl(32.80)*100/2)

lammert

12:19 pm on Aug 16, 2010 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



The conversion to double may give a value like 32.79999.... which after the calculation gives 1639.9999... as result. The conversion to integer then rounds down to the first lower integer value which is 1639.

You can avoid that by adding 0.5 to the intermediate result:

Int(0.5 + (cdbl(32.80)*100/2))

aspdaddy

1:31 pm on Aug 16, 2010 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Thanks lammert, that fixes it for that one amount but breaks it more many other examples :(

Found that cdbl evaulates quite a lot of numbers this way..

astupidname

7:42 am on Aug 17, 2010 (gmt 0)

10+ Year Member



Well, I don't know any vbscript really, but seeing how this is the php forum I'll show my effort in php. Sounded like a fun experiment so I had a go at it. function divvyUp() looks to do it nicely, sure it will come in handy for some group lottery winners!:

<?php

function pre($o) {
echo '<pre style="margin:5px; padding:4px;">'."\n";
print_r(($o === null) ? 'NULL' : $o);
echo "</pre>\n";
}

function divvyUp($amount, $splitTimes) {
if (!is_int($splitTimes) || !(is_int($amount) || is_float($amount)) || $amount / $splitTimes < 0.01) {
return null; //either improper inputs or can not split to a penny or more
}
$n = round($amount / $splitTimes, 2);
$rem = fmod($amount, $n); //remainder of $amount / $n (note mod operator '%' does not work with floats, must use fmod)
//basically DONE by here (the 'predictions' are done, just need to decide what to do with the differential, if any).
//There will be two cases of differential:
//$rem will be within ($splitTimes / 100) of $n,
//or $rem will be less than ($splitTimes / 100).
//(($splitTimes / 100) being number of pennies)
$splits = array_fill(0, $splitTimes, $n);
$randomPersonsIndex = rand(0, $splitTimes - 1); //who are we to decide who the lucky/unlucky one is? Let it be random, I'd say!
if ($rem < $splitTimes / 100) {
$splits[$randomPersonsIndex] += $rem;
} else {
$splits[$randomPersonsIndex] = $rem;
}
return array(
'total' => $amount,
'splitTimes' => $splitTimes,
'normalAmount' => $n,
'offsetAmount' => $splits[$randomPersonsIndex],
'randomLuckyOrUnluckyPerson' => $randomPersonsIndex + 1,
'splits' => $splits
);
}

$tests = array(
array(1.11, 120),
array(16.79, 5),
array(233.55, 17),
array(62.03, 111),
array(2.44, 12),
array(20.20, 4),
array(1298759, 27)
);
foreach ($tests as $arr) {
$a = divvyUp($arr[0], $arr[1]);
pre($a);
}

?>


If anybody's wondering, how to post formatted code on webmasterworld [webmasterworld.com]
Do not copy formatted code on webmasterworld from IE, use other browser such as Firefox.