Подтвердить что ты не робот

Должен ли я использовать NSDecimalNumber для работы с деньгами?

Когда я начал кодировать свое первое приложение, я использовал NSNumber для денежных ценностей, не задумываясь дважды. Тогда я подумал, что, возможно, c типов было достаточно, чтобы справиться с моими ценностями. Тем не менее, мне посоветовали на форуме iPhone SDK использовать NSDecimalNumber из-за его превосходных возможностей округления.

Не будучи математиком по темпераменту, я думал, что парадигма мантиссы/экспоненты может быть излишней; все же, googlin 'around, я понял, что большинство разговоров о деньгах/валюте в cocoa были переданы в NSDecimalNumber.

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

Я на 90% уверен, что мне нужно идти с NSDecimalNumber, но так как я не нашел однозначного ответа в Интернете (что-то вроде: "если вы имеете дело с деньгами, используйте NSDecimalNumber!" ) Я думал, что попрошу здесь. Возможно, ответ очевиден для большинства, но я хочу быть уверенным, прежде чем начать массовое перераспределение моего приложения.

Убедите меня:)

4b9b3361

Ответ 1

Маркус Зарра имеет довольно четкую позицию по этому вопросу: "Если вы имеете дело с валютой вообще, то вы должны использовать NSDecimalNumber" . Его статья вдохновила меня заглянуть в NSDecimalNumber, и я был очень впечатлен этим. Ошибки с плавающей запятой IEEE при работе с математикой 10-го разряда раздражали меня на некоторое время (1 * (0,5-0,4-0,1) = -0,00000000000000002776) и NSDecimalNumber устраняет их.

NSDecimalNumber не просто добавляет еще несколько цифр двоичной точности с плавающей запятой, на самом деле это математика base-10. Это избавляет от ошибок, подобных тем, которые показаны в примере выше.

Теперь я пишу символическое математическое приложение, поэтому мое стремление к точности с точностью до 30 + десятичных цифр и отсутствие странных ошибок с плавающей запятой может быть исключением, но я думаю, что это стоит посмотреть. Операции немного более неудобны, чем простая математика var = 1 + 2, но они все еще управляемы. Если вы беспокоитесь о том, чтобы распределять всевозможные экземпляры во время математических операций, NSDecimal является эквивалентом структуры C NSDecimalNumber, и есть C-функции для выполнения тех же математических операций с ним. По моему опыту, это достаточно быстро для всех, кроме самых требовательных приложений (3 344 593 дополнения/с, 254 017 разделов/с на MacBook Air, 281 555 дополнений/с, 12 027 разделов/с на iPhone).

В качестве дополнительного бонуса NSDecimalNumber descriptionWithLocale: метод предоставляет строку с локализованной версией номера, включая правильный разделитель десятичной дроби. То же самое происходит в обратном порядке для его initWithString: locale: method.

Ответ 2

Да. Вы должны использовать

NSDecimalNumber и

не double или float при работе с валютой на iOS.

Почему это?

Поскольку мы не хотим получать такие вещи, как $9.9999999998 вместо $10

Как это происходит?

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

http://floating-point-gui.de/

В соответствии с яблочными документами

NSDecimalNumber является неизменяемым подклассом NSNumber, предоставляет объектно-ориентированную оболочку для выполнения арифметики base-10. Экземпляр может представлять любое число, которое может быть выражено как показатель mantissa x 10 ^, где мантисса - это десятичное целое число длиной до 38 цифр, а показатель - целое число от -128 до 127.wrapper для выполнения арифметики base-10.

Таким образом, NSDecimalNumber рекомендуется для работы с валютой.

Ответ 3

(Адаптировано из моего комментария к другому ответу.)

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

Единственное правильное решение - NSDecimalNumber (или что-то вроде этого), что ставит проблему на 10 ^ -128 ¢ (т.е., 0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000001 ¢).

(Другой способ - произвольная арифметика, но для этого требуется отдельная библиотека, например библиотека GNU MP Bignum. под LGPL. Я никогда не использовал эту библиотеку и не знаю точно, как она работает, поэтому я не могу сказать, насколько она будет работать для вас.)

[Edit: По-видимому, по крайней мере один человек - Брэд Ларсон - думает, что я говорю о бинарной плавающей точке где-то в этом ответе. Я не.]

Ответ 4

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

Ответ 5

Я нашел удобным использовать целое число для представления числа центов, а затем делить на 100 для представления. Избегает всей проблемы.

Ответ 6

VISA, MasterCards и другие используют целые значения при прохождении сумм. Для отправителя и получателя правильно разбирать amouts в соответствии с показателем валюты (делить или умножать на 10 ^ num, где num - показатель валюты). Обратите внимание, что разные валюты имеют разные показатели. Обычно это 2 (следовательно, мы делим и умножаем на 100), но некоторые валюты имеют показатель = 0 (VND и т.д.) Или = 3.