Равномерно разделите сумму в долларах (десятичную) на целое число

Мне нужно написать процедуру учета для программы, которую я создаю, которая даст мне четное деление десятичной дроби на целое число. Так что например:

$143.13 / 5 =

28.62
28.62
28.63
28.63
28.63

Я видел здесь статью: Равномерно разделить в С #, но похоже, что это работает только для целые деления. Есть идеи об элегантном решении этой проблемы?


person Amberite    schedule 11.01.2010    source источник
comment
Целочисленное деление - это все, что вам нужно. Вы не должны использовать поплавки для денег.   -  person Anon.    schedule 11.01.2010
comment
Здесь следует использовать десятичный тип (отличается от числа с плавающей запятой)   -  person justinlatimer    schedule 11.01.2010
comment
Прочтите ответы на этот вопрос: stackoverflow.com/questions/61872/   -  person Anon.    schedule 11.01.2010
comment
Вероятно, он имеет в виду: сохранить 141.13 как 14113 (int), просто отформатировать его по-другому для отображения или преобразовать в float в самом конце. Есть ли в C # класс Money?   -  person Hamish Grubijan    schedule 11.01.2010
comment
Имеете дело с деньгами и используете для деления числа с плавающей запятой? Вы должны посмотреть Office Space   -  person Anurag    schedule 11.01.2010
comment
Есть десятичный класс ... msdn.microsoft. com / en-us / library / 364x0z75% 28VS.100% 29.aspx, о котором, я надеюсь, он имеет в виду.   -  person RedDeckWins    schedule 11.01.2010


Ответы (7)


Посчитайте суммы по очереди и вычтите каждую сумму из общей, чтобы убедиться, что у вас всегда остается правильная сумма:

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

результат:

28,63
28,62
28,63
28,62
28,63
person Guffa    schedule 11.01.2010
comment
Очень круто! Конечно, нажмите Contract.Requires (total ›= 0), ... divider› 0 или любой другой синтаксис, чтобы сделать это еще более аккуратным. А потом я бы юнит-тестировал эту штуку до смерти ... не помешает. Модульные тесты не обязательно должны выполняться быстро, поэтому получите ответ, затем отсортируйте его и сравните с ожидаемым результатом. Кроме того, я бы вернул сумму вместо того, чтобы печатать ее в фактической реализации. Я бы добавил еще одну функцию, которая, полагаю, возвращает результат в виде массива ... десятичных знаков. Поскольку вы знаете разделитель, вы можете выделить массив нужного размера. - person Hamish Grubijan; 11.01.2010
comment
Просто, но малоэффективно для больших разделителей. - person Marek Stój; 31.05.2010
comment
@Immortal: Да, просто для получения значений неэффективно делать это в цикле, но если вы все равно собираетесь их просматривать, это довольно эффективно. - person Guffa; 31.05.2010

Вы можете решить это (в центах) без построения массива:

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;
person Marcelo Cantos    schedule 11.01.2010
comment
Понятия не имею, почему я был первым, кто проголосовал за. Я считаю, что это наиболее интуитивно понятный и эффективный способ сделать это. - person Ponkadoodle; 11.01.2010
comment
+1. Это отлично сработало для меня в функции базы данных, где другие решения на основе циклов были бы проблемой для производительности. - person dpw; 17.04.2015

Проще иметь дело с центами. Я бы посоветовал вместо 143,13 разделить 14313 на 5 равных частей. Это дает вам 2862 и остаток 3. Вы можете присвоить этот остаток первым трем частям или как хотите. Наконец, конвертируйте центы обратно в доллары.

Также обратите внимание, что вы всегда получите остаток меньше, чем количество деталей, которое вам нужно.

person fsm    schedule 11.01.2010

Прежде всего, убедитесь, что вы не используете числа с плавающей запятой для представления долларов и центов (почему см. Другие сообщения, но простая причина в том, что не все десятичные числа могут быть представлены как числа с плавающей запятой, например, 1,79 доллара США).

Вот один из способов сделать это:

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

Итак, теперь у вас есть нескорректированные суммы 28,62, и затем вам нужно решить, как распределить остаток. Вы можете распределить дополнительную копейку каждому, начиная сверху или снизу (похоже, вы хотите снизу).

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

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

Вы также можете добавить весь остаток к первой или последней записи. Наконец, в зависимости от потребностей бухгалтерского учета, вы также можете создать отдельную транзакцию для остатка.

person Ted M. Young    schedule 11.01.2010

Если у вас есть число с плавающей запятой, точность которого гарантирована ровно две цифры, как насчет этого (псевдокод):

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]++

а затем делайте все, что хотите, с amounts, которые указаны в центах - вы можете преобразовать обратно в числа с плавающей запятой, если вам это абсолютно необходимо, или отформатировать как доллары и центы.

Если не ясно, то смысл всего этого не просто в том, чтобы равномерно разделить плавающую стоимость, а в том, чтобы разделить денежную сумму как можно более равномерно, учитывая, что центы являются неделимой единицей доллара США. В OP: дайте мне знать, если это не то, что вы хотели.

person danben    schedule 11.01.2010
comment
float - это плохо, плохо, плохо ... никогда не следует рекомендовать float для денег - person ; 11.01.2010
comment
Я не виноват, что ОП говорит, что он начинает с поплавков. Если бы вы удосужились прочитать мой ответ, вместо того, чтобы слепо изменять мод, вы бы увидели, что я немедленно преобразовал его в int. - person danben; 11.01.2010

Вы можете использовать алгоритм в вопросе, на который вы ссылаетесь, умножив на 100, используя функцию целочисленного равномерного деления, а затем разделив каждый из результатов на 100 (при условии, что вы хотите обрабатывать только 2 dp, если вы хотите, чтобы 3dp было кратно 1000 и т.д)

person justinlatimer    schedule 11.01.2010
comment
Как и в случае с ответом Ника, это приведет к потере пары центов (в данном примере). - person danben; 11.01.2010

Также можно использовать создание итератора C # для создания Ответ Гуффа удобнее:

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--;
    }
}
person LBushkin    schedule 11.01.2010