この「for」ループは停止しますか、そしてなぜ/なぜ停止しませんか?for(var i = 0; 1 / i> 0; i ++){}


104

このforループは止まりますか?

for (var i=0; 1/i > 0; i++) {
}

もしそうなら、いつ、なぜですか?止まると言われましたが、理由はありませんでした。

更新

調査の一環として、内部で何が行われているのかをすべて説明する非常に長く詳細な記事を書きました-JavaScriptのNumber型について知っておくべきことは次のとおりです


5
止まらない。このコードを実行してみてください。for(var i = 0; 1 / i> 0; i ++){console.log(i)}
Sourabh Agrawal


3
Number.MAX_VALUE + 9.979202e291 == "Infinity"および1 /(NaNまたは 'Infinity'または 'undefined')> 0 == false。
askeet

6
内部にステートメントがないため、JavaScriptはそのループを無視しますか?すなわちそれを離れて最適化しますか?私はそうするいくつかのコンパイルされた言語があることを知っています。
Brian J

3
@ askeet、gotnullや他の人が下で指摘するように、繰り返しインクリメントして無限に到達することはなく、代わりにループに巻き込まれますNumber.MAX_SAFE_INTEGER + 1
LSpice 2016年

回答:


128

(私はメタコンテンツのファンではありませんが、gotnullle_mの回答はどちらも正しいと便利です。これらは元々、このコミュニティWikiが投稿された後に行われた編集でさらにそうです。このCWの元の動機これらの編集の結果として、ほとんどがなくなっていますが、それは有用なままなので、...記載されている執筆者は数人だけですが、他の多くのコミュニティメンバーが、折りたたまれて整理されたコメントで大いに助けてくれました。名前の単なるCWではありません。)


ループは、正しく実装されたJavaScriptエンジンでは停止しません。(エンジンのホスト環境はそれが無限であるため、最終的にそれを終了する可能性がありますが、それは別のことです。)

理由は次のとおりです。

  1. 最初i01/i > 0isの場合、JavaScriptでは1/0is Infinity、and、およびtrueであるため、条件はtrue Infinity > 0です。

  2. その後、iは増加し、長い間正の整数値として増加し続けます(さらに9,007,199,254,740,991反復)。これらすべてのケースで、1/iは残ります> 0(ただし、の値は最後に向かって非常に小さく1/iなります!)。そのため、ループはi、値に到達するループまで継続しますNumber.MAX_SAFE_INTEGER

  3. JavaScriptの数値は、IEEE-754倍精度2進浮動小数点数であり、かなりコンパクトな形式(64ビット)であり、高速な計算と広い範囲を提供します。これは、数値を符号ビット、11ビットの指数、および52ビットの仮数として格納することによって行われます(ただし、賢いため、実際には53ビットの精度になります)。これは2進数(基数2)の浮動小数点です。仮数(およびいくらかの賢さ)は値を与え、指数は数の大きさを与えます。

    当然ながら、有効ビットが非常に多いため、すべての数値を保存できるわけではありません。ここ数1、及びフォーマットが格納することができることを1後の次の最大数、1 + 2 -52 ≈1.00000000000000022、その1 + 2×2の後の次の最高-52 ≈1.00000000000000044。

       + ------------------------------------------------- --------------符号ビット
      / + ------- + ---------------------------------------- --------------指数
     / / | + ------------------------------------------------- +-仮数
    / / | / |
    0 01111111111 0000000000000000000000000000000000000000000000000000
                    = 1
    0 01111111111 0000000000000000000000000000000000000000000000000000000001
                    ≈1.00000000000000022
    0 01111111111 0000000000000000000000000000000000000000000000000010
                    ≈1.00000000000000044
    

    1.00000000000000022から1.00000000000000044へのジャンプに注意してください。1.0000000000000003を保存する方法はありません。これは整数でも発生する可能性があります。Number.MAX_SAFE_INTEGER(9,007,199,254,740,991)は、形式が保持できる正の最大整数値でiありi + 1、両方とも正確に表現できます(spec)。9,007,199,254,740,991と9,007,199,254,740,992の両方を表すことができますが、次の整数9,007,199,254,740,993は表すことができません。9,007,199,254,740,992の後に表すことができる次の整数は9,007,199,254,740,994です。以下はビットパターンです。右端(最下位)のビットに注意してください。

       + ------------------------------------------------- --------------符号ビット
      / + ------- + ---------------------------------------- --------------指数
     / / | + ------------------------------------------------- +-仮数
    / / | / |
    0 10000110011 1111111111111111111111111111111111111111111111111111
                    = 9007199254740991(Number.MAX_SAFE_INTEGER)
    0 10000110100 0000000000000000000000000000000000000000000000000000
                    = 9007199254740992(Number.MAX_SAFE_INTEGER + 1)
    x xxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
                      9007199254740993(Number.MAX_SAFE_INTEGER + 2)は保存できません
    0 10000110100 0000000000000000000000000000000000000000000000000000000001
                    = 9007199254740994(Number.MAX_SAFE_INTEGER + 3)
    

    形式は基数2であり、その指数を使用すると、最下位ビットは小数ではなくなります。値は2です。オフ(9,007,199,254,740,992)またはオン(9,007,199,254,740,994)にすることができます。そのため、この時点で、整数(整数)スケールでも精度が失われ始めています。これはループに影響を与えます!

  4. i = 9,007,199,254,740,992ループが完了したら、再びi++...を与えますi = 9,007,199,254,740,992i次の整数は格納できず、計算結果は切り捨てられるため、に変化はありません。i変更すると変更されますがi += 2i++変更できません。したがって、定常状態に到達しました。i決して変化せず、ループが終了することはありません。

関連するさまざまな計算を以下に示します。

if (!Number.MAX_SAFE_INTEGER) {
  // Browser doesn't have the Number.MAX_SAFE_INTEGER
  // property; shim it. Should use Object.defineProperty
  // but hey, maybe it's so old it doesn't have that either
  Number.MAX_SAFE_INTEGER = 9007199254740991;
}
var i = 0;
console.log(i, 1/i, 1/i > 0); // 0, Infinity, true
i++;
console.log(i, 1/i, 1/i > 0); // 1, 1, true
// ...eventually i is incremented all the way to Number.MAX_SAFE_INTEGER
i = Number.MAX_SAFE_INTEGER;
console.log(i, 1/i, 1/i > 0); // 9007199254740991 1.1102230246251568e-16, true
i++;
console.log(i, 1/i, 1/i > 0); // 9007199254740992 1.1102230246251565e-16, true
i++;
console.log(i, 1/i, 1/i > 0); // 9007199254740992 1.1102230246251565e-16, true (no change)
console.log(i == i + 1);      // true


79

回答:

条件1/i > 0は常にtrueと評価されます。

  • 最初はそれが理由本当だ1/0評価さにInfinityし、Infinity > 0真であります

  • 以来、それは真のまま1/i > 0、すべてのために真であるi < Infinityi++達することはありませんInfinity

なぜi++到達しないのInfinityですか?Numberデータ型の精度に制限があるため、次のような値がありi + 1 == iます。

9007199254740992 + 1 == 9007199254740992 // true

一度i値が(に相当することに達する)、それも後に同じままになります。Number.MAX_SAFE_INTEGER + 1i++

したがって、無限ループがあります。


付録:

なんで9007199254740992 + 1 == 9007199254740992

JavaScriptのNumberデータ型は、実際には64ビットのIEEE 754倍精度浮動小数点です。それぞれNumberは、3つの部分(1ビットの符号、11ビットの指数、52ビットの仮数)として分解されて格納されます。その値は-1 符号 ×仮数×2 指数です。

9007199254740992はどのように表されますか?1.0×2 53、またはバイナリで:

ここに画像の説明を入力してください

仮数の最下位ビットをインクリメントすると、次に大きい数が得られます。

ここに画像の説明を入力してください

その数の値は1.00000000000000022…×2 53 = 9007199254740994です

どういう意味ですか?Number900719925474099 2または900719925474099 4のいずれかになりますが、間には何もありません。

さて、900719925474099 2 + 1を表すためにどれを選択しますか?IEEE 754丸め規則は 900719925474099:答え与える2


9
短くて正しい、現在受け入れられている回答よりも優れている
AlexWien

@AlexWien承認済みの回答は、コミュニティWikiの承認済みの回答です。
fulvio 2016年

2
「コミュニティーWikiの容認者」という用語の答えはわかりません。これはstackoverflowとどう関係していますか?これが外部リンクの場合、リンクを提供する必要があります。stackoverflowで受け入れられた回答は常に変更される可能性があり、ステータスは最終ではありません。
AlexWien

「なぜi ++が無限に到達しないのですか?Numberデータ型の精度が限られているため...」<-無限の精度の数値型を使用しても、無限に到達することはありません。無限大:P
Blorgbeardは

1
@Blorgbeard 制限された精度の倍精度で無限に数えることができますfor (var i = 0; i < Infinity; i += 1E306);。たとえば、1よりはるかに大きい数だけインクリメントする必要があります。しかし、私はあなたがどこから来たのかを
知る

27

このNumber.MAX_SAFE_INTEGER定数は、JavaScriptでの最大安全整数を表します。MAX_SAFE_INTEGER定数は、の値を持ちます9007199254740991。その数値の背後にある理由は、JavaScript がIEEE 754で指定されている倍精度浮動小数点形式の数値を使用し、-(2 53-1)と2 53-1の間の数値のみを安全に表すことができるためです。

この文脈での安全とは、整数を正確に表し、それらを正しく比較する機能を指します。たとえば、Number.MAX_SAFE_INTEGER + 1 === Number.MAX_SAFE_INTEGER + 2はに評価されますがtrue、これは数学的に正しくありません。詳細についてはNumber.isSafeInteger()、を参照してください。

MAX_SAFE_INTEGERはの静的プロパティであるため、作成したオブジェクトのプロパティNumberとしてNumber.MAX_SAFE_INTEGERではなく、常にとして使用しNumberます。

更新:

削除された回答の誰かが言及しました:i無限に達することは決してありません。に達するNumber.MAX_SAFE_INTEGERi++、変数をインクリメントしなくなります。これは実際に正しくありません

@TJクラウダーは、コメントi = Number.MAX_SAFE_INTEGER; i++; i == Number.MAX_SAFE_INTEGER;ですfalse。しかし、次の反復は不変の状態に到達するため、mainの答えは正しいです。

i例では決して到達しませんInfinity


2
具体的に9007199254740992 + 19007199254740992です。
Kobi

1
@GerardoFurtado私はそれを想像します。
fulvio 2016年

1
@GerardoFurtado for (var i=0; NaN > 0; i++) { console.log(i); }は何も生成しません。
fulvio 2016年

2
@GerardoFurtado:その場合、ループは停止します。ループ本体は非常に最初のテスト(以降、すべてで入力することはないでしょう1/i > 0)場合以来、偽だろうiである01/iあるNaN、とNaN > 0falseです。
TJクラウダー2016年

1
@TJCrowder回答を更新しました。指摘してくれてありがとう!
fulvio 2016年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.