3

2つの整数を比較しようとしています。1つはの、もう1つrowNSIndexPathcountですNSArray。はrow0に等しく、は2に等しい。次のようなステートメントにcountそれらがあります。if

if([self.selectedSetpoint row] < ([self.theCategories count]-3))
{
    //Do true stuff
}

したがって、私の数学では、if比較しているので、ステートメントは偽であるはずです0 < -1。しかし、私はこのステートメントを真として通し続けており、内部のブロックifが実行されています。NSLog間違った値を取得しているだけではないことを確認するために、値を試してみました。if私はステートメントの前にこれを正しく配置しました:

NSLog(@"%d",[self.selectedSetpoint row]);
NSLog(@"%d",[self.theCategories count]-3);
NSLog(@"%@",[self.selectedSetpoint row] < ([self.theCategories count]-3)?@"Yes":@"No");

コンソールでこれを取得しました:

2012-07-17 08:58:46.061 App[61345:11603] 0
2012-07-17 08:58:46.061 App[61345:11603] -1
2012-07-17 08:58:46.062 App[61345:11603] Yes

この比較が真実である理由は何ですか?整数の比較について何か誤解していますか?

これのすぐ上に、比較する別のifステートメントがあります

[self.selectedSetpoint row]<([self.theCategories count]-2)

これは0 < 0、であり、正常に動作します(returns NO)。ですから、私が得ていない負の整数の使用に問題があるように感じます。

前もって感謝します。

4

4 に答える 4

6

問題は、の戻り値がcount符号なし整数であり、その大きさよりも多くを引くと、アンダーフローして非常に大きくなることだと思います。私はいくつかのテストを実行しましたが、あなたと同じ基本的な動作が得られます(それはそうであるように見え-1、特定のコンテキストでは期待どおりに機能しているように見えif()ます...しかし、ブロックのコンテキストでは明らかにアンダーフローしています。

ばかげた問題ですが、幸いなことに簡単な解決策があります。ifステートメントの所定の位置にキャストします。

if([self.selectedSetpoint row] < ( (int)[self.theCategories count] -3 ))
{
    //Do true stuff
}
于 2012-07-17T14:27:11.443 に答える
2

私は別の解決策を提案しようとしていました-つまり、減算の使用を避け、代わりに方程式の反対側で加算を使用することです:

if ([self.selectedSetpoint row] + 3 < [self.theCategories count])
{
    //Do true stuff
}

これは、この種のアンダーフローバグに巻き込まれることを回避しますが、別の落とし穴はそのままになります...つまり、この質問への回答で参照されている変換ルール:Cでさまざまなデータ型を比較す​​るための一般的なルールは何ですか?

その質問への回答から引用すると、C99仕様には次のように記載されています。

  • (一方のオペランドが符号付きでもう一方が符号なしの場合)それ以外の場合、符号なし整数型のオペランドのランクがもう一方のオペランドの型のランク以上の場合、符号付き整数型のオペランドは次の型に変換されます。符号なし整数型のオペランド。

したがって、の値が負の場合[self.selectedSetpoint row] + 3、比較は失敗します...

ここでの他の回答は、(NSUInteger)から(NSInteger)へのキャストを提唱していますが、符号なしの値が非常に大きい場合、オーバーフローの問題が発生する可能性があることに注意してください。例:

(NSInteger) -3 < (NSInteger) 4294967289 == false...

これを解決する簡単な方法があるに違いないと考えて、私は最初にそれを解決する難しい方法を思いつきました...

#define SafeLT(X, Y) \
({ typeof (X) _X = (X); \
   typeof (Y) _Y = (Y); \
( _X < (NSInteger) 0 ? (( _Y > 0) ? YES : _X < _Y ) : ( _Y < (NSInteger) 0 ? NO : _X < _Y));})

これは、NSUIntegerとNSIntegerの組み合わせに関係なく機能するはずであり、オペランドが最大で1回評価されるようにします(効率のため)。

正当性の証明として:

  1. Trueと評価するには_X < (NSInteger) 0、_XはNSIntegerで<0でなければならないため、_Y> 0かどうかを確認します。コンパイラは、_Yの型を評価することにより、ここで正しい比較を行います。Y> 0の場合、定義上、YESを返します。そうでなければ、XとYの両方が符号付きで<0であることがわかり、それらを安全に比較できます。
  2. ただし、XがNSUIntegerまたは> 0の場合、Yをテストして<0かどうかを確認します。Yが<0の場合、定義上、NOを返します。これで、XはNSUIntegerまたはNSInteger> 0になり、YはNSUIntegerまたはNSInteger> 0になります。混合比較はNSUIntegerにプロモートされるため、アンダーフローの可能性がないため、毎回安全に変換できます。

ただし、より簡単な解決策は、より長い符号付き整数に型キャストすることです(システムに整数がある場合)。

#define EasySafeLT(X, Y) \
({ long long _X = (X); \
   long long _Y = (Y); \
   ( _X < _Y);})

これは、より大きなタイプが利用可能であるかどうかに依存しますが、常に実行可能であるとは限りません。

于 2012-07-17T17:33:19.377 に答える
0

これは、signedintとunsignedintを比較する場合のようです。あなたが署名されて印刷される数を求めているので、あなたのログステートメントはあなたの仮定を投げます。

NSLog(@"%d", -1); //Signed

-1;

NSLog(@"%u", -1); //Unsigned (tested on ios, 32 bit ints)

4294967295;

そして、比較すると、0 < 4294967295確かに真実です。

@ctraheyが示唆するようにキャストすると、問題が解決するはずです。

if([self.selectedSetpoint row] < ( (int)[self.theCategories count] -3 ))
{
    //Do true stuff
}
于 2012-07-17T14:31:03.957 に答える
0

問題は、countプロパティが一種でNSUIntegerあり、小さい数から大きい数を引くと、ゼロ未満の数が得られず、非常に大きな正の数が得られるため、奇妙な動作が発生することです。

この方法を試してみると、良い結果が得られます。

NSLog(@"%@",(NSInteger)[self.selectedSetpoint row] < ((NSInteger)[self.theCategories count]-3)?@"Yes":@"No");
于 2012-07-17T14:33:12.800 に答える