Vous étiez sur la bonne voie (avec des réserves, voir ci-dessous). Vous devriez être en mesure d'utiliser seulement la initialisers de NSNumber comme ils sont hérités par NSDecimalNumber.
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]);
Plus d'info sur ce sujet peuvent être trouvées ici.
Cependant, j'ai vu beaucoup de discussions sur un Débordement de Pile et sur le web à propos de problèmes avec l'initialisation NSDecimalNumber. Il semble y avoir certaines questions relatives à la précision et à la conversion/de chambres doubles avec NSDecimalNumber
. En particulier lorsque vous utilisez l'initialisation des membres hérité de NSNumber.
J'ai frappé le test ci-dessous:
double dbl = 36.76662445068359375000;
id xx1 = [NSDecimalNumber numberWithDouble: dbl]; // Don't do this
id xx2 = [[[NSDecimalNumber alloc] initWithDouble: dbl] autorelease];
id xx3 = [NSDecimalNumber decimalNumberWithString:@"36.76662445068359375000"];
id xx4 = [NSDecimalNumber decimalNumberWithMantissa:3676662445068359375L exponent:-17 isNegative:NO];
NSLog(@"raw doubleValue: %.20f, %.17f", dbl, dbl);
NSLog(@"xx1 doubleValue: %.20f, description: %@", [xx1 doubleValue], xx1);
NSLog(@"xx2 doubleValue: %.20f, description: %@", [xx2 doubleValue], xx2);
NSLog(@"xx3 doubleValue: %.20f, description: %@", [xx3 doubleValue], xx3);
NSLog(@"xx4 doubleValue: %.20f, description: %@", [xx4 doubleValue], xx4);
La sortie est:
raw doubleValue: 36.76662445068359375000 36.76662445068359375
xx1 doubleValue: 36.76662445068357953915, description: 36.76662445068359168
xx2 doubleValue: 36.76662445068357953915, description: 36.76662445068359168
xx3 doubleValue: 36.76662445068357953915, description: 36.76662445068359375
xx4 doubleValue: 36.76662445068357953915, description: 36.76662445068359375
Donc vous pouvez voir que lors de l'utilisation de l' numberWithDouble
méthode de convenance sur NSNumber
(que vous ne devriez pas vraiment à utiliser grâce à lui retourner le mauvais type de pointeur) et même de l'initialiser initWithDouble
(que l'OMI "devrait" être OK pour appeler), vous obtenez un NSDecimalNumber avec une représentation interne (retourné par l'appel de description
sur l'objet) ce n'est pas aussi précis que celui que vous obtenez lorsque vous appelez decimalNumberWithString:
ou decimalNumberWithMantissa:exponent:isNegative:
.
Notez également que de le convertir en un lit double en appelant doubleValue
sur le NSDecimalNumber
de l'instance est en train de perdre de la précision, mais, fait intéressant, le même quelle que soit la initialiser vous appeler.
Donc au final, je pense que c'est vous recommandons d'utiliser l'un de l' decimalNumberWith*
méthodes déclarées à l' NSDecimalNumber
le niveau de la classe pour créer votre NSDecimalNumber cas lorsque l'on traite avec une grande précision des nombres à virgule flottante.
Alors, comment appelez-vous ces initialisers facilement quand vous avez un double, float ou d'autres NSNumber?
Deux méthodes décrites ici "travailler", mais encore la précision des questions.
Si vous avez déjà le nombre stocké comme un NSNumber alors cela devrait fonctionner:
id n = [NSNumber numberWithDouble:dbl];
id dn1 = [NSDecimalNumber decimalNumberWithDecimal:[n decimalValue]];
NSLog(@" n doubleValue: %.20f, description: %@", [n doubleValue], n);
NSLog(@"dn1 doubleValue: %.20f, description: %@", [dn1 doubleValue], dn1);
Mais comme vous pouvez le voir à partir de la sortie ci-dessous il lops une partie de la moins de chiffres significatifs:
n doubleValue: 36.76662445068359375000, description: 36.76662445068359
dn1 doubleValue: 36.76662445068357953915, description: 36.76662445068359
Si le nombre est une primitive (float ou double), cela devrait fonctionner:
id dn2 = [NSDecimalNumber decimalNumberWithMantissa:(dbl * pow(10, 17))
exponent:-17
isNegative:(dbl < 0 ? YES : NO)];
NSLog(@"dn2 doubleValue: %.20f, description: %@", [dn2 doubleValue], dn2);
Mais, vous obtiendrez des erreurs de précision de nouveau. Comme vous pouvez le voir dans la sortie ci-dessous:
dn2 doubleValue: 36.76662445068357953915, description: 36.76662445068359168
La raison pour laquelle je pense que pour cette précision perte est due à la participation de la virgule flottante se multiplient, parce que le code suivant fonctionne:
id dn3 = [NSDecimalNumber decimalNumberWithMantissa:3676662445068359375L
exponent:-17
isNegative:NO];
NSLog(@"dn3 doubleValue: %.20f, description: %@", [dn3 doubleValue], dn3);
Sortie:
dn3 doubleValue: 36.76662445068357953915, description: 36.76662445068359375
Donc, le plus systématiquement la précision de la conversion/initialisation à partir d'un double
ou float
de NSDecimalNumber est à l'aide de decimalNumberWithString:
. Mais, comme Brad Larson a souligné, dans sa réponse, cela pourrait être un peu lent. Sa technique pour la conversion en utilisant NSScanner peut-être mieux si la performance est un problème.