2012-02-09 20 views
7

私はいくつかの数値比較をしようとしています。私は奇妙な結果を得ています。NSNumber比較:異なる結果を返す

NSNumber* number1 = [NSNumber numberWithFloat:1.004]; 
NSNumber* number2 = [NSNumber numberWithDouble:1.004]; 

([number1 compare:number2] == NSOrderedSame) ? NSLog(@"YES") : NSLog(@"NO"); 
([number1 compare:number2] == NSOrderedAscending) ? NSLog(@"YES") : NSLog(@"NO"); 
([number1 doubleValue] == [number2 doubleValue]) ? NSLog(@"YES") : NSLog(@"NO"); 
([number1 floatValue] == [number2 floatValue]) ? NSLog(@"YES") : NSLog(@"NO"); 

ログ出力:

NO
YES
NO
YES

これは私にとって非常にイライラさせられます。私はこれがおそらく浮動小数点数のビット数と2倍のビット数との差のためであることを知っています。それは私には、比較を行うために倍精度浮動小数点に切り捨てているようだ。しかし、数字がどのように作成されているのかわからない場合、どうすれば正しい結果が得られますか? NSNumberを比較する別の方法はありますか?

+3

あなたが使用している場合について、 '[数値1 isEqualToNumber:数値2] 'それは、少なくともあなたが価値の平等を確かめようとしているのであれば、好ましい方法です。 – twilson

+1

申し訳ありませんtwilsonは、まだNOを返します。番号は異なります。 – picciano

答えて

7

を与えます。それらが同じでない理由は、1.004が正確にバイナリで表現できないためです。 doubleの近似値は、float近似値よりも小数点以下の桁数が多いため、2つの数値が異なります。

NSNumber* number1 = [NSNumber numberWithFloat:1.004]; 
NSNumber* number2 = [NSNumber numberWithDouble:1.004]; 

NSLog(@"number 1: %.12f", [number1 doubleValue]); 
NSLog(@"number 2: %.12f", [number2 doubleValue]); 

([number1 isEqualToNumber:number2]) ? NSLog(@"YES") : NSLog(@"NO"); 
fabs([number1 floatValue] - [number2 doubleValue]) < 1.0e-7 ? NSLog(@"YES") : NSLog(@"NO"); 

結果:浮動小数点数を比較する際に通常、あなたは彼らが許容値fabs(a - b) < tolerance内に等しいかどうかをテスト

2012-02-09 15:08:34.272 so9219935[3313:903] number 1: 1.003999948502 
2012-02-09 15:08:34.274 so9219935[3313:903] number 2: 1.004000000000 
2012-02-09 15:08:34.275 so9219935[3313:903] NO 
2012-02-09 15:08:34.275 so9219935[3313:903] YES 
+0

非常に良い解決策! –

+5

Apple(より正確に言えば、コンパイラライター)は、プログラマの等価性に対する許容性について仮定することはできません。彼らができることは、プログラマーが彼らに言ったこととまったく同じです。この場合、2つの不等な値を比較していました。浮動小数点数は簡単なものではありません。時間がある場合は、「あらゆるコンピュータ科学者が浮動小数点演算について知っておくべきこと」(http://www-users.math.umd.edu/~jkolesar/mait613/floating_point_math.pdf)を見てください。 – SSteve

+0

参考にしていただきありがとうございます。私はこのことについてお勧めの読書を探してきました。私はしばらくこの問題について知っていましたが、それについては何もしていません。 :) –

2

compare:メソッドは、型変換の標準のCルールに従います。たとえば、整数値を持つNSNumberオブジェクトと浮動小数点値を持つNSNumberオブジェクトを比較すると、整数値は比較のために浮動小数点値に変換されます。

また、NSNumberを浮動小数点で初期化してから、double型に変換すると精度が失われます。

NSNumber* number1 = [NSNumber numberWithFloat:1.004]; 
NSNumber* number2 = [NSNumber numberWithDouble:1.004]; 
NSLog(@"number1 double: %1.16f", [number1 doubleValue]); 
NSLog(@"number2 double: %1.16f", [number2 doubleValue]); 
NSLog(@"number1 objCType %s", [number1 objCType]); 
NSLog(@"number2 objCType %s", [number2 objCType]); 

2012-02-09 15:59:49.487 testNSNumber[89283:f803] number1 double: 1.0039999485015869 
2012-02-09 15:59:49.488 testNSNumber[89283:f803] number2 double: 1.0040000000000000 
2012-02-09 16:21:01.655 testNSNumber[4351:f803] number1 objCType f 
2012-02-09 16:21:01.656 testNSNumber[4351:f803] number2 objCType d 

あなたがfloatとdoubleの混在していることがわかっている場合は、一つの解決策は、あなたがあなたの質問であなたのコードスニペットの最後の行に行ったようにするNSNumberのfloatValuesを比較することです。

また、objCTypeメソッドを使用してNSNumberのデータの型を取得することもできます。

+0

私は本当にそのobjTypeが好きです!ありがとうございました! –

2

isEqualToNumberを試してください。

([number1 isEqualToNumber:number2]) ? NSLog(@"YES") : NSLog(@"NO"); 

this stackoverflow questionあなたの状態をチェックする方法も私はisEqualToNumber:を使用してみました、それはNOを返した詳細な回答に

+0

これは期待どおりに機能せず、 "NO"を記録します。 floatとdoubleで初期化されたNSNumberは異なる値になります。上記の私の答えを参照してください。あなたが参照しているSOの質問は、すべてのフロートを使用しています。 – picciano

関連する問題