Evenly divide a dollar amount (decimal) by an inte

2019-03-09 08:13发布

问题:

I need to write an accounting routine for a program I am building that will give me an even division of a decimal by an integer. So that for example:

$143.13 / 5 =

28.62
28.62
28.63
28.63
28.63

I have seen the article here: Evenly divide in c#, but it seems like it only works for integer divisions. Any idea of an elegant solution to this problem?

回答1:

Calculate the amounts one at a time, and subtract each amount from the total to make sure that you always have the correct total left:

decimal total = 143.13m;
int divider = 5;
while (divider > 0) {
  decimal amount = Math.Round(total / divider, 2);
  Console.WriteLine(amount);
  total -= amount;
  divider--;
}

result:

28,63
28,62
28,63
28,62
28,63


回答2:

You can solve this (in cents) without constructing an array:

int a = 100 * amount;
int low_value = a / n;
int high_value = low_value + 1;
int num_highs = a % n;
int num_lows = n - num_highs;


回答3:

It's easier to deal with cents. I would suggest that instead of 143.13, you divide 14313 into 5 equal parts. Which gives you 2862 and a remainder of 3. You can assign this remainder to the first three parts or any way you like. Finally, convert the cents back to dollars.

Also notice that you will always get a remainder less than the number of parts you want.



回答4:

First of all, make sure you don't use a floating point number to represent dollars and cents (see other posts for why, but the simple reason is that not all decimal numbers can be represented as floats, e.g., $1.79).

Here's one way of doing it:

decimal total = 143.13m;
int numberOfEntries = 5;
decimal unadjustedEntryAmount = total / numberOfEntries;
decimal leftoverAmount = total - (unadjustedEntryAmount * numberOfEntries);
int numberOfPenniesToDistribute = leftoverAmount * 100;
int numberOfUnadjustedEntries = numberOfEntries - numberOfPenniesToDistribute;

So now you have the unadjusted amounts of 28.62, and then you have to decide how to distribute the remainder. You can either distribute an extra penny to each one starting at the top or at the bottom (looks like you want from the bottom).

for (int i = 0; i < numberOfUnadjustedEntries; i++) {
  Console.WriteLine(unadjustedEntryAmount);
}

for (int i = 0; i < numberOfPenniesToDistribute; i++) {
  Console.WriteLine(unadjustedEntryAmount + 0.01m);
}

You could also add the entire remainder to the first or last entries. Finally, depending on the accounting needs, you could also create a separate transaction for the remainder.



回答5:

If you have a float that is guaranteed exactly two digits of precision, what about this (pseudocode):

amount = amount * 100 (convert to cents)
int[] amounts = new int[divisor]
for (i = 0; i < divisor; i++) amounts[i] = amount / divisor
extra = amount % divisor
for (i = 0; i < extra; i++) amounts[i]++

and then do whatever you want with amounts, which are in cents - you could convert back to floats if you absolutely had to, or format as dollars and cents.

If not clear, the point of all this is not just to divide a float value evenly but to divide a monetary amount as evenly as possible, given that cents are an indivisible unit of USD. To the OP: let me know if this isn't what you wanted.



回答6:

You can use the algorithm in the question you're referencing by multipling by 100, using the integer evenly divide function, and then dividing each of the results by 100 (assuming you only want to handle 2 dp, if you want 3dp multiple by 1000 etc)



回答7:

Take a look at this question: What is the best data type to use for money in c#?

Wait a second! You want to make sure that not a single cent gets lost, right?



回答8:

It is also possible to use C# iterator generation to make Guffa's answer more convenient:

public static IEnumerable<decimal> Divide(decimal amount, int numBuckets)
{
    while(numBuckets > 0)
    {
        // determine the next amount to return...
        var partialAmount = Math.Round(amount / numBuckets, 2);
        yield return partialAmount;
        // reduce th remaining amount and #buckets
        // to account for previously yielded values
        amount -= partialAmount;
        numBuckets--;
    }
}