2013-08-25 1 views
2

J'ai une méthode qui reçoit un float comme argument, et vérifie si ce flottant est dans un tableau. Pour ce faire, je convertis d'abord le flotteur en NSNumber. Ceci est une simplification testable de mon code:Vérification si un tableau contaisObject: @ (float) dans Objective-C peut retourner de façon inattendue NO

float aFloat = 0.3; 
NSNumber *aNSNumber = @(aFloat); 

NSArray *anArray = @[@(0.0), @(0.3), @(1.0)]; 
NSLog(@"%d", [anArray containsObject:aNSNumber]); 

Ce code consignera 0 (à savoir NO), il est donc dire que 0.3 est pas anArray. Si aFloat est un nombre "rond" tel que 0.0, 0.5, ou 1.0, le test fonctionne et enregistre 1 (c'est-à-dire YES). Tout nombre autre que celui-ci, comme le 0.3 ci-dessus, échoue.

D'autre part, si nous changeons aFloat pour être un double, cela fonctionne. Ou, si nous changeons anArray à ceci:

NSArray *array = @[[NSNumber numberWithFloat:0.0], [NSNumber numberWithFloat:0.3], [NSNumber numberWithFloat:1.0]]; 

Il fonctionne également. Ce que je suppose, c'est que la notation NSNumber @() crée un numberWithDouble:. Mais, ma question est, ne devrait pas fonctionner même lorsque aFloat est un float? Comme je le "convertis" de toute façon en l'enregistrant dans aNSNumber ... Et ne devrait-il pas automatiquement reconnaître que le float0.3 et le double0.3 sont réellement les mêmes numéros? Aussi, pourquoi les chiffres "ronds" fonctionnent quand même?

+1

float et double ne sont pas du même type, alors pourquoi NSNumber aurait-il la peine de les hacher comme le même type? – CodaFi

+0

@CodaFi car après que je les ai créés, ils devraient tous être le même NSNumber. À mon avis, peu importe comment vous avez créé le NSNumber - ils représentent le même nombre "réel" (0.3 dans ce cas), donc le NSNumber créé avec le "float" 0.3 devrait être le même que le NSNumber créé avec le 'double' 0.3. Ils ne devraient être différentiables qu'après avoir obtenu leurs valeurs de NSNumber en utilisant 'floatValue' ou' doubleValue'. – glevco

+0

Pourquoi diable utilisez-vous le flotteur et pas le double? N'utilisez jamais le flotteur - sauf si vous pouvez donner une bonne raison de le faire. Dans ce cas, ça vous a mordu dans le cul. Et pour le compilateur, votre opinion ne compte pas. Surtout quand tu as tort. – gnasher729

Répondre

3

@(0.3) utilise -numberWithDouble: car 0.3 a le type double. Si vous avez écrit @(0.3f), il utilisera -numberWithFloat:.

Ni float ni double ne peuvent stocker exactement 0,3. (Il est similaire au problème d'écriture 1/3 sous forme décimale - vous ne pouvez pas le faire exactement en utilisant un nombre fini de chiffres.) Au lieu de cela, vous obtenez le float le plus proche de 0,3 et le double le plus proche de 0,3. Ces deux nombres ne sont pas égaux, donc -containsObject: ne trouve pas de correspondance.

Les deux float et double peuvent stocker exactement 0.0 et 1.0, donc les deux conversions donnent le même résultat et réussit.

1

Le @(0.3) dans le anArray est un double enveloppé dans un NSNumber. Et bien sûr, votre aFloat est un float enveloppé dans un NSNumber.

Essayez l'un des deux changements possibles:

1) Changer float aFloat à double aFloat

ou

2) Changer @(0.3) dans anArray-@(0.3f).

+0

Merci, mais j'ai déjà ces solutions comme je l'ai dit dans ma question. Ce que je cherche est en fait une sorte de "pourquoi" cela fonctionne comme ça ... Voir mon dernier paragraphe pour référence. – glevco

1

Vous pouvez visualiser la différence entre 0,3 et 0.3f avec cet extrait:

NSLog(@"%.20f %.20f", 0.3, 0.3f); 

Mon débogueur montre: 0,29999999999999998890 0,30000001192092895508

assez drôle, le « 0.3 » apparaît plus précis que le « 0.3f '. Quoi qu'il en soit, je spéculer que le compilateur peut prendre le droit 0.3 comme un double en premier avant de le convertir en un flotteur tandis que la spécification 0.3f est un flottant immédiatement.

Une autre chose à observer est si vous aviez fait cela:

NSArray *anArray = @[@(0.0), aNSNumber, @(1.0)]; 

L'appel containsObject aurait réussi. Peut-être que la route du compilateur pour convertir le flottant en tout ce qu'il utilise en interne entre la déclaration aFloat et le littéral a quelques différences. Je spécule juste ici ...

En général, je n'aime pas tester l'égalité avec les flotteurs ou les doubles d'ailleurs. Les problèmes d'exactitude peuvent à travers vous pour une boucle trop facilement.

+1

"Assez drôle, le '0.3' semble plus précis que le '0.3f'". Rien d'étrange à ce sujet. '0.3' est un' double' (type flottant double précision) tandis que '0.3f' est un' float' (type flottant simple précision). Je dirais que c'est très attendu. –

+0

Ah ne le savais pas. Je suppose que cela a du sens que le double est la valeur par défaut du compilateur. – Yohst

+1

Ce n'est pas spécifiquement la valeur par défaut du compilateur. Il est défini par la spécification de langage C. –

Questions connexes