Question

Lorsque j'ai commencé à coder ma première application, j'ai utilisé NSNumber pour des valeurs monétaires sans y réfléchir à deux fois. Ensuite, j'ai pensé que les types c suffisaient peut-être pour gérer mes valeurs. Pourtant, le forum du SDK pour iPhone m'a conseillé d'utiliser NSDecimalNumber, en raison de ses excellentes capacités d'arrondi.

N'étant pas un mathématicien de tempérament, je pensais que le paradigme mantisse / exposant pourrait être exagéré; Malgré tout, je me suis rendu compte que la plupart des discussions sur l’argent / la monnaie dans le cacao étaient renvoyées à NSDecimalNumber.

Notez que l'application sur laquelle je travaille va être internationalisée. Par conséquent, la possibilité de compter le montant en cents n'est pas vraiment viable, car la structure monétaire dépend grandement de la localisation utilisée.

Je suis sûr à 90% que je dois utiliser NSDecimalNumber, mais comme je n’ai trouvé aucune réponse claire sur le Web (quelque chose comme: "si vous traitez avec de l’argent, utilisez NSDecimalNumber!"), je pensais demander ici. La réponse est peut-être évidente pour la plupart des gens, mais je veux être certaine avant de commencer une refactorisation massive de mon application.

Convainquez-moi:)

Était-ce utile?

La solution

Marcus Zarra a une position assez claire à ce sujet: " Si vous utilisez des devises, vous devriez utiliser NSDecimalNumber. " Son article m'a inspiré pour examiner NSDecimalNumber, et j'ai été très impressionné par il. Les erreurs de virgule flottante IEEE lorsqu’il s’agit de calculs en base 10 me irritent depuis un certain temps (1 * (0.5 - 0.4 - 0.1) = -0.00000000000000002776) et NSDecimalNumber les supprime.

NSDecimalNumber n’ajoute pas que quelques chiffres de précision binaire en virgule flottante, il effectue les calculs en base 10. Cela supprime les erreurs telles que celle montrée dans l'exemple ci-dessus.

Maintenant, je suis en train d’écrire une application mathématique symbolique, alors mon désir d’une précision de plus de 30 décimales et d’aucune erreur en virgule flottante pourrait être une exception, mais je pense que cela vaut la peine d’être examiné. Les opérations sont un peu plus délicates que les simples mathématiques de style var = 1 + 2, mais elles sont toujours gérables. Si vous souhaitez allouer toutes sortes d'instances lors de vos opérations mathématiques, NSDecimal est l'équivalent C de NSDecimalNumber et il existe des fonctions C pour effectuer exactement les mêmes opérations mathématiques avec elle. D'après mon expérience, ces applications sont rapides, à l'exception des applications les plus exigeantes (3 344 593 additions / s, 254 017 divisions / s sur un MacBook Air, 281 555 additions / s, 12 027 divisions / s sur un iPhone).

En prime, la méthode descriptionWithLocale: de NSDecimalNumber fournit une chaîne avec une version localisée du nombre, y compris le séparateur décimal correct. Il en va de même pour sa méthode initWithString: locale: method.

Autres conseils

Oui. Vous devez utiliser

NSDecimalNumber et

ne pas doubler ou flotter lorsque vous traitez avec des devises sur iOS.

Pourquoi est-ce que c'est ??

Parce que nous ne voulons pas obtenir des choses comme 9,9999999998 $ au lieu de 10 $

Comment ça se passe ??

Les flotteurs et les doubles sont des approximations. Ils viennent toujours avec une erreur d'arrondi. Le format utilisé par les ordinateurs pour stocker les décimales est à l’origine de cette erreur. Si vous avez besoin de plus de détails, lisez

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

Selon Apple docs,

NSDecimalNumber est une sous-classe immuable de NSNumber. Elle fournit un wrapper orienté objet permettant de réaliser des calculs arithmétiques en base 10. Une instance peut représenter n’importe quel nombre pouvant être exprimé sous la forme d’une mantisse x 10 ^ exposant, où mantisse est un entier décimal allant jusqu’à 38 chiffres, et exposant est un entier compris entre –128 et 127.wrapper pour l’arithmétique en base 10.

NSDecimalNumber est donc recommandé pour traiter les devises.

(Adapté de mon commentaire sur l'autre réponse.)

Oui, vous devriez. Un nombre entier de centimes ne fonctionne que tant que vous n'avez pas besoin de représenter, par exemple, un demi-cent. Si cela se produit, vous pouvez le changer pour compter un demi-centime, mais que se passera-t-il si vous devez alors représenter un quart de cent ou un huitième de cent?

La seule solution appropriée est NSDecimalNumber (ou quelque chose du genre), qui reporte le problème à 10 ^ -128 & # 162; (i.e.,
0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

000000000000000000000000000000000000001 & # 162;).

(Une autre solution serait l'arithmétique de précision arbitraire, mais cela nécessite une bibliothèque distincte, telle que la bibliothèque GNU MP Bignum

[Edit: apparemment, au moins une personne & # 8212; Brad Larson & # 8212; pense que je parle de virgule flottante binaire quelque part dans cette réponse. Je ne suis pas.]

Une meilleure question est de savoir quand vous ne pas utiliser NSDecimalNumber pour gérer de l'argent. La réponse courte à cette question est la suivante: lorsque vous ne pouvez pas tolérer la surcharge de performances de NSDecimalNumber et que vous vous moquez des petites erreurs d’arrondi car vous n’avez jamais affaire à plus de quelques chiffres de précision. La réponse encore plus courte est que vous devez toujours utiliser NSDecimalNumber pour gérer de l'argent.

J'ai trouvé pratique d'utiliser un nombre entier pour représenter le nombre de centimes, puis de le diviser par 100 pour la présentation. Évite toute la question.

VISA, MasterCard et autres utilisent des valeurs entières pour transmettre des montants. Il appartient à l’envoyeur et au destinataire d’analyser correctement les dépenses en fonction de l’exposant de la devise (diviser ou multiplier par 10 ^ num, où num - est un exposant de la devise). Notez que les différentes devises ont des exposants différents. Il s’agit généralement de 2 (par conséquent, nous divisons et multiplions par 100), mais certaines devises ont un exposant = 0 (VND, etc.), ou = 3.

scroll top