Какой тип данных лучше всего использовать для денег в C #?

426

Какой тип данных лучше всего использовать для денег в C #?

NotDan
источник
4
Вы можете найти ответы из этого поста полезными.
Нтомбела
Вот сопоставление для всех типов данных: docs.microsoft.com/en-us/dotnet/framework/data/adonet/…
JohnLBevan
Кроме того, если используются аннотации данных, включитеusing System.ComponentModel.DataAnnotations; ... [DataType(DataType.Currency)] msdn.microsoft.com/en-us/library/…
JohnLBevan

Ответы:

422

Как это описано в десятичном виде как:

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

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

decimal myMoney = 300.5m;
Ли Тревейл
источник
41
Вы должны объяснить, что по этой ссылке важно. Ответ должен быть достаточно хорошим сам по себе, со ссылкой в ​​качестве дополнительной ссылки или детали. См stackoverflow.com/help/how-to-answer
TheRubberDuck
2
Таким образом, ответ минимальной длины может содержать меньше символов, чем комментарий минимальной длины - интересно! Не то чтобы у меня была проблема с кратким / кратким ответом, особенно когда он также «глубокий» в том смысле, что он связан с дальнейшим обсуждением.
Б. Клэй Шеннон
3
Удивительный ответ, и я не думаю, что он нуждается в дополнительном объяснении, поскольку он полностью отвечает на вопрос. Ссылка на документацию MSDN является бонусом, насколько я понимаю. Браво!
trnelson
@ Ли Тревейл, на что похожи деньги (9.0098) означает 4 символа после Очка
SAR
114

System.Decimal

Тип десятичного значения представляет десятичные числа в диапазоне от положительных 79,228,162,514,264,337,593,543,950,335 до отрицательных 79,228,162,514,264,337,593,543,950,335. Тип десятичного значения подходит для финансовых расчетов, требующих большого количества значащих целых и дробных цифр и без ошибок округления. Тип Decimal не устраняет необходимость округления. Скорее, это минимизирует ошибки из-за округления.

Я хотел бы указать на этот превосходный ответ от zneak о том, почему не следует использовать double.

Дэвид Уолшотс
источник
68

Используйте шаблон Money из Patterns of Enterprise Application Architecture ; укажите сумму в десятичном виде и валюту в виде перечисления.

lmsasu
источник
2
На самом деле я собирался предложить это, но я делаю валюту классом, чтобы я мог определить обменный курс (по отношению к «базовой валюте», часто доллару США [который я установил, чтобы иметь обменный курс 1,00]).
Томас Оуэнс,
5
Для будущих посетителей этой ветки (как и я) есть вот это: nuget.org/packages/Money и это круто !
Корин
Интересно, должен ли такой тип быть структурой или классом. Десятичное число + перечисление (int) делает его 20 байтов. Мои деньги еще на структуре.
Nawfal
У этого Moneynuget есть мертвая ссылка на github для сайта проекта, так что ... нет документов?
Джордж Мауэр
Проблема в том, что если вы создаете свою собственную реализацию, вы должны выяснить, как на самом деле ее сохранить. А самый популярный ORM (EF) вообще не поддерживает пользовательские типы данных. Поэтому кто - то попросил , чтобы получить действительно глубоко в сорняках , чтобы делать то , что должно быть довольно простой вещью.
Джордж Мауэр
25

Десятичный. Если вы выбираете double, вы оставляете себя открытым для ошибок округления

SquidScareMe
источник
8
@Jess doubleможет привести к ошибкам округления, потому что с плавающей точкой невозможно точно представить все числа (например, 0,01 не имеет точного представления в плавающей точке). Decimalс другой стороны, действительно представляет числа точно . (Компромисс Decimalимеет меньший диапазон, чем с плавающей запятой). Плавающая точка может дать вам * непреднамеренные * ошибки округления (например 0.01+0.01 != 0.02). Decimalможет дать вам ошибки округления, но только когда вы попросили об этом (например, Math.Round(0.01+0.02)возвращает ноль)
Ян Бойд
2
@IanBoyd: значение «$ 1,57» может быть точно представлено (удвоено) 157. Если кто-то использует doubleи тщательно применяет масштабирование и округление, зависящее от предметной области, когда это уместно, оно может быть совершенно точным. Если кто-то неаккуратен в округлении, он decimalможет дать результаты, которые семантически неверны (например, если вы сложите несколько значений, которые должны быть округлены до ближайшей копейки, но на самом деле их сначала нет вокруг). Единственная хорошая вещь о decimalтом, что масштабирование встроено.
суперкат
1
@supercat, относительно этого комментария «если сложить несколько значений, которые должны быть округлены до ближайшей копейки, но на самом деле их сначала нет вокруг», я не вижу, как поплавок решит это. Это ошибка пользователя и никак не связана с десятичными. я понимаю, но я чувствую, что это было неуместно, в основном потому, что ИанБойд определил это ... если вы попросите об этом.
sawe
13

Согласитесь с шаблоном «Деньги»: работать с валютами слишком сложно, если вы используете десятичные дроби.

Если вы создаете класс Currency, вы можете поместить туда всю логику, связанную с деньгами, включая правильный метод ToString (), больший контроль над разбором значений и лучший контроль над делениями.

Кроме того, с классом Currency нет никакой возможности непреднамеренно смешать деньги с другими данными.

Lennaert
источник
10

Другой вариант (особенно если вы катаетесь в своем собственном классе) - использовать int или int64 и обозначить четыре младшие цифры (или, возможно, даже 2) как «справа от десятичной точки». Так что «по краям» вам понадобится немного «* 10000» на входе и немного «/ 10000» на выходе. Это механизм хранения, используемый Microsoft SQL Server, см. Http://msdn.microsoft.com/en-au/library/ms179882.aspx.

Изюминка в том, что все ваше суммирование может быть выполнено с использованием (быстрой) целочисленной арифметики.

DSZ
источник
7

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

Это предположение может основываться на другом предположении, что приложение никогда не будет использоваться в других странах с разными валютами. Я видел случаи, когда это оказалось ложным.

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

Некоторые люди скажут, что создание или даже использование типа только для денег - это «золотое покрытие» или добавление дополнительной сложности сверх известных требований. Я категорически не согласен. Чем более вездесуща концепция в вашей области, тем важнее приложить разумные усилия, чтобы использовать правильную абстракцию заранее. Если вы хотите увидеть сложность, попробуйте работать в приложении, которое раньше использовало, decimalи теперь Currencyрядом с каждым decimalсвойством есть дополнительное свойство.

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

И это не так сложно использовать что-то кроме десятичной. Google "Nuget Money Type", и вы увидите, что многие разработчики создали такие абстракции (в том числе и я). Это легко. Это так же просто, как использовать DateTimeвместо сохранения даты в string.

Скотт Ханнен
источник
5

Создайте свой собственный класс. Это кажется странным, но тип .Net не подходит для разных валют.

Ноэль Кеннеди
источник