1. ホーム
  2. javascript

[解決済み] この「for」ループは停止するのか、その理由と停止しない理由 for (var i=0; 1/i > 0; i++) { }.

2022-12-21 10:09:47

質問

これは for のループは止まらないのでしょうか?

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

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

更新情報

調査の一環として、ボンネットの下で何が起こっているのかをすべて説明する、かなり長くて詳細な記事を書きました。 JavaScript の Number 型について知っておくべきことは、以下のとおりです。

どのように解決するのですか?

(メタコンテンツは苦手ですが gotnullの le_m の の答えは、どちらも正しくて有用です。それらはもともと、そして もそうですが このコミュニティWikiが投稿された後に行われた編集で、そうなりました。このCWの当初の動機は、これらの編集の結果、ほとんどなくなってしまいましたが、有用であることに変わりはありませんから...。また 著者は数名しかいませんが、他の多くのコミュニティメンバーがコメントを寄せてくれて、それを折り込み、整理してくれています。これは名前だけのCWではありません)。


正しく実装されたJavaScriptエンジンでは、ループは止まりません。 (エンジンのホスト環境では、無限ループのため最終的に終了してしまうかもしれませんが、それは別の話です)。

理由はこうです。

  1. 当初は i0 という条件であれば 1/i > 0 が真になるのは、JavaScriptでは 1/0Infinity であり、かつ Infinity > 0 は真である。

  2. その後に i はインクリメントされ、正の整数値として長い時間(さらに 9,007,199,254,740,991 回)成長し続けるでしょう。それらのすべてのケースで 1/i はそのまま > 0 (の値です(ただし 1/i を取得します。 本当に を小さくする!)ので、ループは i という値になるループまで続きます。 Number.MAX_SAFE_INTEGER .

  3. JavaScript の数値は IEEE-754 倍精度バイナリ浮動小数点で、かなりコンパクトなフォーマット (64 ビット) ですが、高速な計算と広大な範囲を提供します。これは、符号ビット、11 ビットの指数、52 ビットのシグニフィカントとして数値を格納することで実現されています (ただし、巧妙な方法で実際には 53 ビットの精度を得ています)。これは バイナリ (ベース2)浮動小数点です。シグニフィカンド (およびいくつかの巧妙な処理) が値を与え、指数がその数の大きさを与えます。

    当然ながら、これだけ多くの有効ビットがあれば、すべての数を格納できるわけではありません。以下は、数値 1 と、このフォーマットが格納できる 1 の次に大きい数値である 1 + 2 です。 -52 ≈ 1.00000000000000022, そしてその次に大きい数字 1 + 2 × 2 -52 ≈ 1.00000000000000044:

       +--------------------------------------------------------------- sign bit
      / +-------+------------------------------------------------------ exponent
     / / | +-------------------------------------------------+- シグニフィカント
    
    0 01111111111 0000000000000000000000000000000000000000000000000000
                    = 1
    0 01111111111 0000000000000000000000000000000000000000000000000001
                    ≈ 1.00000000000000022
    0 01111111111 0000000000000000000000000000000000000000000000000010
                    ≈ 1.00000000000000044
    
    

    1.00000000000000022から1.00000000000000044にジャンプしていることに注意してください。1.0000000000000003を格納する方法はありません。これは整数でも起こりうることです。 Number.MAX_SAFE_INTEGER (9,007,199,254,740,991) は、このフォーマットが保持できる正の整数の最大値で、ここで ii + 1 はどちらも正確に表現できる ( スペック ). 9,007,199,254,740,991 と 9,007,199,254,740,992 の両方が表現可能であるが 9,007,199,254,740,992の次に表現できる整数は9,007,199,254,740,994となります。以下はそのビットパターンです。右端(最下位)のビットに注意してください。

       +--------------------------------------------------------------- sign bit
      / +-------+------------------------------------------------------ exponent
     / / | +-------------------------------------------------+- シグニフィカント
    
    
    0 10000110011 1111111111111111111111111111111111111111111111111111
                    = 9007199254740991 (Number.MAX_SAFE_INTEGER)
    0 10000110100 0000000000000000000000000000000000000000000000000000
                    = 9007199254740992 (Number.MAX_SAFE_INTEGER + 1)
    x xxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx。
                      9007199254740993 (Number.MAX_SAFE_INTEGER + 2) cannot be stored
    0 10000110100 0000000000000000000000000000000000000000000000000001
                    = 9007199254740994 (Number.MAX_SAFE_INTEGER + 3)
    
    

    この形式は基数2であり、この指数では最下位ビットはもはや小数ではありません。これはループに影響を与えます。

  4. を完了した後 i = 9,007,199,254,740,992 のループに入る。 i++ は私たちを与える... i = 9,007,199,254,740,992 には何の変化もありません。 i には変化がありません。なぜなら、次の整数を格納することができず、計算が切り捨てになってしまうからです。 i を実行すると、次のように変化します。 i += 2 をした場合、しかし i++ は変更できません。つまり定常状態になったわけです。 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