while(i == i + 1){}が永久にループする間、iの値は何ですか


120

私は英国の大学の試験で、高度なプログラミングコースからこの謎を解きました

次のループについて考えてみます。ここでは、私は今のところ宣言されていません。

while (i == i + 1) {}

whileループが永遠に続くように、iこのループに先行するの定義を見つけます

このコードスニペットに対して同じ質問をした次の質問:

while (i != i) {}

私には明らかでした。もちろん、この他の状況ではNaNそうですが、私は本当に前の状況に行き詰まっています。これはオーバーフローと関係がありますか?そのようなループがJavaで永遠にループする原因は何ですか?


3
.equals()メソッドをオーバーライドする可能性はありますか?iは宣言されていないため、必要なクラスを使用できます。
Geno Chen

7
@Raedwaldが「非専門的」なコードを研究することで、より「専門的」になるので...とにかく、それは良い質問です
Andrew Tobilko

12
楽しい事実は、C#で、これはまた、その値であり、NULL可能な数値型のために働くnullことから、null == null真である、とnull + 1ありますnull
Eric Lippert

4
@EricDuminil:状況はあなたが想像するよりもはるかに悪いです。多くの言語では、浮動小数点演算は少なくとも doubleで指定された64ビットの精度で実行する必要があります。つまり、コンパイラーの気まぐれでより高い精度で実行でき、実際にはこれが発生します。0.2 + 0.1 == 0.3コンパイラーの設定や月の満ち欠けなどによってなぜ値が変わるのか疑問に思っているC#プログラマーからの、このサイトのダースの質問を紹介します。
Eric Lippert

5
@EricDuminil:この混乱の原因はIntelにあります。Intelは、数値を登録できる場合に高精度で高速な浮動小数点演算を実行するチップセットを提供しました。オプティマイザのレジスタスケジューラが今日どのように機能するかについて。言語デザイナーとしてのあなたの選択は、反復可能な計算高速で正確な計算のどちらかであり、浮動小数点演算に関心を持つコミュニティは後者を選択します。
Eric Lippert

回答:


142

まず、while (i == i + 1) {}ループはの値を変更しないため、iこのループを無限にすることは、iを満たす値を選択することと同じi == i + 1です。

そのような多くの値があります:

「エキゾチック」なものから始めましょう:

double i = Double.POSITIVE_INFINITY;

または

double i =  Double.NEGATIVE_INFINITY;

これらの値が満たされる理由i == i + 1は、
JLS 15.18.2に記載されています。数値型の加算演算子(+および-)

無限大と有限値の合計は、無限オペランドと同じです。

有限値を無限値に追加すると、無限値になるはずなので、これは当然のことです。

とは言っても、iを満たす値のほとんどは、i == i + 1単に大きいdouble(またはfloat)値です。

例えば:

double i = Double.MAX_VALUE;

または

double i = 1000000000000000000.0;

または

float i = 1000000000000000000.0f;

doubleおよびfloat種類は、あなたが十分に大きいかかりそうだとすれば、限られた精度を持っているdoubleか、float追加して、値を1同じ値になりますそれに。


10
または(double)(1L<<53)-またはfloat i = (float)(1<<24)
dave_thompson_085

3
@ルスラン:どんな数学者も反対するでしょう。浮動小数点数はほとんど意味がありません。それらは、関連性がなく、再帰的ではなく(NaN!= NaN)、置換さえできません(-0 == 0、ただし1/0!= 1 / -0)。したがって、代数の機構のほとんどは適用できません。
ケビン

2
@Kevin浮動小数点数は実際には一般的にあまり意味がありませんが、無限大の動作(その文で説明されているもの)は意味をなすように設計されています。
ルスラン

4
@Kevin floatに公平を期すために、無限大または未定義の値を扱う場合、代数でリストしたプロパティも想定できません。
Voo 2018年

2
@Kevin:私見、浮動小数点演算は、「正と負のゼロ」記号の正、負、および符号なしの「無限小」の概念を1つの「真のゼロ」と置き換えて、 NaNはそれ自体と同じです。真のゼロは、すべての場合において加法恒等式として動作する可能性があり、無限小による除算を含む何らかの演算は、無限小が正であると仮定する方向へのバイアスを失います。
スーパーキャット2018年

64

これらのパズルについては、Joshua BlochとNeal Gafterによる「Java Puzzlers:Traps、Pitfalls、and Corner Cases」の本で詳しく説明されています。

double i = Double.POSITIVE_INFINITY;
while (i == i + 1) {}

または:

double i = 1.0e40;
while (i == i + 1) {}

どちらも無限ループになり1ます。これは、十分に大きい浮動小数点値に追加しても値が変化しないためです。これは、後続の「ギャップを埋める」ことがないためです1です。

2番目のパズルに関するメモ(将来の読者向け):

double i = Double.NaN;
while (i != i) {}

また、NaNはそれ自体を含む浮動小数点値2と等しくないため、無限ループになります。


1-Javaパズル:トラップ、落とし穴、コーナーケース(第4章-ルーピーパズル)。

2- JLS§15.21.1



0

ただのアイデア:ブール値はどうですか?

bool i = TRUE;

これはそうではありませんi + 1 == iか?


言語に依存します。多くの言語は、intと組み合わせるとブール値を自動的にintに変換します。他の人はあなたが提案するように行います-intをブール値に強制します。
Carl Witthoft、2018年

8
この質問はJavaの質問であり、あなたの提案はJavaでのコンパイルに合格しません(これには+、a booleanintasをオペランドとして取る演算子がありません)。
エラン2018年

@エラン:それが演算子のオーバーロードの全体的な考え方です。Javaのブール値をC ++のブール値のように動作させることができます。
ドミニク

4
ただし、Javaは演算子のオーバーロードをサポートしていないため、サポートできません。
CupawnTae
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.