Лучшие практики работы с денежными суммами в C#

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

Прочитав эти статьи, каковы лучшие практики и рекомендации по работе с деньгами в C#? Должен ли я использовать определенный тип данных для небольшого количества и другой для большего количества? Кроме того, я живу в Великобритании, что означает, что мы используем , (например, 4000 фунтов стерлингов, тогда как в других культурах одна и та же сумма представлена ​​по-разному).


person GurdeepS    schedule 01.03.2009    source источник


Ответы (7)


Вы не должны использовать числа с плавающей запятой из-за ошибок округления. Десятичный тип должен вам подойти.

person Ed S.    schedule 01.03.2009
comment
Я проголосовал за, чтобы компенсировать голосование против. Я не думаю, что голоса против должны быть разрешены без комментариев. - person Jeff Reddy; 06.12.2012

Десятичный — наиболее разумный тип для денежных сумм.

Decimal — это числовой тип с основанием 10 с плавающей запятой с точностью до 28 и более десятичных цифр. При использовании Decimal у вас будет меньше сюрпризов, чем при использовании типа Double с основанием 2.

Double использует вдвое меньше памяти, чем Decimal, и Double будет намного быстрее из-за аппаратного обеспечения ЦП для многих распространенных операций с плавающей запятой, но он не может точно представлять большинство дробей с основанием 10 (например, 1,05) и имеет менее точные 15+ десятичных цифр точность. Double имеет преимущество в большем диапазоне (он может представлять большие и меньшие числа), что может пригодиться для некоторых вычислений, особенно для некоторых статистических вычислений.

Один ответ на ваш вопрос гласит, что Decimal - это фиксированная точка с 4 десятичными цифрами. Это не тот случай. Если вы сомневаетесь в этом, обратите внимание, что следующая строка кода дает 0,0000000001:

Console.WriteLine("number={0}", 1m / 10000000000m);

Сказав все это, интересно отметить, что наиболее широко используемая в мире программа для работы с денежными суммами, Microsoft Excel, использует двойные числа. Конечно, им приходится прыгать через множество обручей, чтобы заставить его работать хорошо, и это все еще оставляет желать лучшего. Попробуйте эти две формулы в Excel:

  • =1-0.9-0.1
  • =(1-0.9-0.1)

Первый дает 0, второй дает ~-2,77e-17. Excel на самом деле массирует числа при сложении и вычитании чисел в некоторых случаях, но не во всех случаях.

person Joe Erickson    schedule 01.03.2009
comment
Десятичная за деньги это хорошо. Но соотношение денег все же должно быть двойным (например, процентные ставки). - person Richard; 01.03.2009

Мартин Фаулер рекомендует использовать денежный класс. Ссылка на обоснование. Существует несколько реализаций его идеи, или вы можете написать свою собственную. Собственная реализация Фаулера написана на Java, поэтому он использует класс. Версии C#, которые я видел, используют структуру, что кажется разумным.

person dan-gph    schedule 01.03.2009
comment
Эта ссылка 404 :( - person BoD; 27.11.2019

Я использую объект значения для хранения как суммы (как decimal), так и валюты. Это позволяет работать с разными валютами одновременно. decimal — рекомендуемый тип данных для денег в .NET.

person R. Martinho Fernandes    schedule 01.03.2009

Я бы рекомендовал использовать Decimal, как рекомендуют другие, если требуется деление. Для простого приложения подсчета я бы рекомендовал тип Integer. Для обоих типов я всегда работал бы с наименьшим денежным номиналом. (т.е. центов в Канаде/США)

Мне нравится теория вызова денег Фаулера, добавленная @dangph.

person tylermac    schedule 01.03.2009

Как вы указали в своем вопросе, помимо использования соответствующего типа данных, важно, насколько хорошо ваша программа будет обрабатывать конвертацию валюты. Этот вопрос, конечно, если не исключительно валюты. Джефф Этвуд сделал отличный пост, в котором резюмировал достоинства исполнения Тест индейки.

person ahsteele    schedule 01.03.2009

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

Однажды я потратил неделю на отслеживание ошибки в 1 цент, потому что SQLServer и .Net используют разные способы округления валют, а приложение не было единообразным в том, как оно обрабатывало определенные типы вычислений — иногда они выполнялись в SQL, иногда в .net. Изучите «банковское округление", если вам интересно.

Есть также проблемы, связанные с форматированием валют - не уверен, что вам придется иметь дело с суммами, не относящимися к Великобритании, другими языками / культурами и т. д., но это добавит еще один уровень сложности.

person chris    schedule 01.03.2009
comment
Еще одна проблема, на которую следует обратить внимание, заключается в том, что SQL Server хранит DATETIME с точностью до 3,33 миллисекунды (0,00333 секунды). Однажды меня это укусило, так как этого было недостаточно для моих целей. - person ahsteele; 01.03.2009