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

StackOverflow https://stackoverflow.com/questions/421463

Вопрос

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

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

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

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

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

Это было полезно?

Решение

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

NSDecimalNumber не просто добавляет еще несколько цифр двоичной точности с плавающей запятой, он фактически выполняет математику с начальным значением 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: метод.

Другие советы

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

NSDecimalNumber и

нет двойной или плавать когда вы имеете дело с валютой на iOS.

Почему это??

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

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

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

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

Согласно документам Apple,

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

Поэтому NSDecimalNumber рекомендуется для работы с валютой.

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

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

Единственное правильное решение - NSDecimalNumber (или что-то подобное), которое откладывает проблему на 10 ^ -128 & # 162; (То есть,
0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000001 & # 162;.)

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top