Dois-je utiliser NSDecimalNumber à traiter avec de l'argent?
Que j'ai commencé à coder ma première application que j'ai utilisé NSNumber pour les valeurs de la monnaie sans réfléchir à deux fois. Puis j'ai pensé que peut-être c types ont été assez à faire avec mes valeurs. Pourtant, j'ai été conseillé dans le SDK de l'iPhone forum à utiliser NSDecimalNumber, en raison de son excellente arrondissement capacités.
Pas être mathématicien, par tempérament, je pensais que la mantisse/exposant paradigme peut-être exagéré; encore, googlin' autour, j'ai réalisé que la plupart parle d'argent/monnaie de cacao ont été mentionnés NSDecimalNumber.
Avis que l'application que je suis en train de travailler sur va être internationalisées, de sorte que la possibilité de compter le montant en centimes, c'est pas vraiment viable, de la politique monétaire de la structure dépend de la locale utilisée.
Je suis sûr à 90% que j'ai besoin d'aller avec NSDecimalNumber, mais depuis je n'ai pas trouvé de réponse précise sur le web (quelque chose comme: "si vous faites affaire avec de l'argent, de l'utilisation NSDecimalNumber!") Je pensais que je voudrais poser ici. Peut-être que la réponse est évidente pour la plupart, mais je veux être sûr avant de commencer un massif de re-factoring de mon application.
Me convaincre 🙂
Vous devez vous connecter pour publier un commentaire.
Marcus Zarra a une jolie position claire sur ce point: "Si vous traitez avec de la monnaie à tous, alors vous devriez être en utilisant NSDecimalNumber." Son article m'a incité à regarder dans NSDecimalNumber, et j'ai été très impressionné. En virgule flottante IEEE erreurs lorsque vous traitez avec base-10 mathématiques ont été irritant de moi pendant un moment (1 * (0.5 - 0.4 - 0.1) = -0.00000000000000002776) et NSDecimalNumber n'loin avec eux.
NSDecimalNumber n'est pas juste ajouter un autre quelques chiffres de binaire à virgule flottante de précision, il ne fait en base 10 de mathématiques. Cela se débarrasser des erreurs comme montré dans l'exemple ci-dessus.
Maintenant, je suis de l'écriture symbolique mathématique de l'application, de sorte que mon désir de 30+ les chiffres de précision et plus d'étranges virgule flottante erreurs peut-être une exception, mais je pense qu'il vaut la peine de regarder. Les opérations sont un peu plus difficile que de simples var = 1 + 2 style de mathématiques, mais ils sont encore gérables. Si vous êtes inquiet au sujet de l'allocation de toutes sortes de cas au cours de vos opérations mathématiques, NSDecimal est la structure C équivalent de NSDecimalNumber et il existe des fonctions C pour faire exactement les mêmes opérations mathématiques avec elle. Dans mon expérience, ce sont beaucoup rapide pour tous, mais les applications les plus exigeantes (3,344,593 ajouts/s, 254,017 divisions/s sur un MacBook Air, 281,555 ajouts/s, 12,027 divisions/s sur un iPhone).
Comme un bonus supplémentaire, NSDecimalNumber de descriptionWithLocale: méthode fournit une chaîne de caractères avec une version localisée de le numéro, y compris le bon séparateur décimal. Il en va de même en sens inverse pour son initWithString:paramètres: la méthode.
Oui. Vous devez utiliser
NSDecimalNumber et
pas double ou float lorsque vous traitez avec de la monnaie sur iOS.
Pourquoi est-ce??
Parce que nous ne voulons pas faire les choses comme $9.9999999998 au lieu de $10
Comment ça se passe??
Chars et les doubles sont des approximations. Ils ont toujours livré avec une erreur d'arrondi. Le format ordinateurs utilisent pour stocker les décimales cause de cette rouding erreur.
Si vous avez besoin de plus de détails, lisez
http://floating-point-gui.de/
Selon apple docs,
NSDecimalNumber est immuable sous-classe de NSNumber, fournit une orientée objet wrapper pour faire en base 10 de l'arithmétique. Un exemple peut représenter n'importe quel nombre qui peut être exprimé comme la mantisse x 10^exposant où la mantisse est un nombre entier décimal jusqu'à 38 chiffres de long, et l'exposant est un entier allant de -128 à 127.wrapper pour faire en base 10 de l'arithmétique.
Donc NSDecimalNumber est recommonded pour faire face à la monnaie.
(Adapté de mon commentaire sur l'autre réponse.)
Oui, vous devriez le faire. Intégrante nombre de pièces d'un cent ne fonctionne que tant que vous n'avez pas besoin de représenter, disons, un demi-cent. Si cela se produit, vous pouvez le changer à compter de la demi-cents, mais que faire si vous devez représenter un quart de cent, ou un huitième de un cent?
La seule bonne solution est NSDecimalNumber (ou quelque chose comme ça), qui met du problème à 10^-128¢ (c'est à dire,
0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000001¢).
(Une autre façon serait arbitraire calcul en précision, mais qui nécessite une bibliothèque distincte, comme la GNU MP Bignum bibliothèque. GMP est sous LGPL. Je n'ai jamais utilisé cette bibliothèque et ne sais pas exactement comment il fonctionne, donc je ne pourrais pas dire comment il pourrait travailler pour vous.)
[Edit: Apparemment, au moins une personne—Brad Larson pense que je parle de binaire à virgule flottante quelque part dans cette réponse. Je ne suis pas.]
NSDecimalNumber
stocke ses significande huit uint16 s, pour un maximum de significande 2^128 (~3.4 × 10^38),Une meilleure question est, quand devez-vous pas utilisation NSDecimalNumber à traiter avec de l'argent. La réponse courte à cette question est, quand vous ne pouvez pas tolérer la charge de NSDecimalNumber et vous n'avez pas de soins sur les petites erreurs d'arrondi parce que vous n'êtes jamais de plus de quelques chiffres de précision. La plus courte réponse est, vous devriez toujours utilisation NSDecimalNumber lorsque vous traitez avec de l'argent.
J'ai trouvé plus pratique d'utiliser un entier correspondant au nombre de cents, puis diviser par 100 pour la présentation. Évite l'ensemble de la question.
VISA, Mastercard et d'autres sont à l'aide de valeurs de type entier en passant montants. C'est à l'expéditeur et le récepteur à analyser amouts correctement en fonction de la monnaie exposant (diviser ou multiplier par 10^num, où num est un exposant de la monnaie). Notez que les différentes monnaies des différents exposants. Habituellement, il est de 2 (donc de nous diviser et de se multiplier par 100), mais certaines monnaies ont exposant = 0 (VND,etc), ou = 3.