Je construis une application qui doit effectuer des calculs d'argent.
Je me demande comment utiliser correctement NSDecimalNumber, en particulier comment l'initialiser à partir d'entiers, de flottants et de doubles?
J'ai seulement trouvé facile d'utiliser la méthode -decimalNumberWithString:
. Les méthodes -initWith...
sont déconseillées de manière à ne laisser que celles avec mantisse, mais jamais dans les 7 langues que j'ai utilisées auparavant, j'en avais besoin, alors je ne sais pas ce qui est mis là-bas ...
Ne PAS utilisez les méthodes +numberWith...
de NSNumber
pour créer des objets NSDecimalNumber
. Ils sont déclarés comme retournant des objets NSNumber
et il n'est pas garanti qu'ils fonctionnent comme des instances NSDecimalNumber
.
Ceci est expliqué dans ce thread par Bill Bumgarner, développeur chez Apple. Je vous encourage à signaler un bogue contre ce problème, en faisant référence au bogue rdar: // 6487304.
En guise d'alternative, ce sont toutes les méthodes appropriées à utiliser pour créer un NSDecimalNumber
:
+ (NSDecimalNumber *)decimalNumberWithMantissa:(unsigned long long)mantissa
exponent:(short)exponent isNegative:(BOOL)flag;
+ (NSDecimalNumber *)decimalNumberWithDecimal:(NSDecimal)dcm;
+ (NSDecimalNumber *)decimalNumberWithString:(NSString *)numberValue;
+ (NSDecimalNumber *)decimalNumberWithString:(NSString *)numberValue locale:(id)locale;
+ (NSDecimalNumber *)zero;
+ (NSDecimalNumber *)one;
+ (NSDecimalNumber *)minimumDecimalNumber;
+ (NSDecimalNumber *)maximumDecimalNumber;
+ (NSDecimalNumber *)notANumber;
Si vous voulez simplement une constante NSDecimalNumber
à partir d'une constante float
ou int
, essayez ceci:
NSDecimalNumber *dn = [NSDecimalNumber decimalNumberWithDecimal:
[[NSNumber numberWithFloat:2.75f] decimalValue];
La bonne façon est en fait de faire ceci:
NSDecimalNumber *floatDecimal = [[[NSDecimalNumber alloc] initWithFloat:42.13f] autorelease];
NSDecimalNumber *doubleDecimal = [[[NSDecimalNumber alloc] initWithDouble:53.1234] autorelease];
NSDecimalNumber *intDecimal = [[[NSDecimalNumber alloc] initWithInt:53] autorelease];
NSLog(@"floatDecimal floatValue=%6.3f", [floatDecimal floatValue]);
NSLog(@"doubleDecimal doubleValue=%6.3f", [doubleDecimal doubleValue]);
NSLog(@"intDecimal intValue=%d", [intDecimal intValue]);
Voir plus d'infos ici .
En ce qui concerne la conception, évitez de convertir NSDecimalNumber ou NSDecimals en valeurs int, float et double pour les mêmes raisons, il est recommandé d’utiliser NSDecimalNumbers: perte de précision et problèmes de représentation en virgule flottante binaire. Je sais que, parfois, c'est inévitable (prendre les entrées d'un curseur, faire des calculs trigonométriques, etc.), mais vous devriez essayer de prendre les utilisateurs comme NSStrings, puis utiliser initWithString: locale: ou decimalNumberWithString: locale: pour générer NSDecimalNumbers. Faites tous vos calculs avec NSDecimalNumbers et renvoyez leur représentation aux utilisateurs ou sauvegardez-les dans SQLite (ou ailleurs) en tant que description de chaîne à l'aide de descriptionWithLocale :.
Si vous devez entrer un int, un float ou un double, vous pouvez faire quelque chose comme ceci:
int myInt = 3;
NSDecimalNumber *newDecimal = [NSDecimalNumber decimalNumberWithString:[NSString stringWithFormat:@"%d", myInt]];
ou vous pouvez suivre la suggestion d'Ashley pour vous assurer que vous êtes en sécurité dans la construction décimale.
Un petit ajout: si vous initiez NSDecimalNumber
à partir d'une chaîne, il pourrait être utile de définir également une langue Par exemple, si votre chaîne contient une virgule en tant que decimal separator
.
self.order.amount = [NSDecimalNumber decimalNumberWithString:self.amountText locale:[NSLocale currentLocale]];