JS におけるメモリと変数の保存についての詳細な説明

JS におけるメモリと変数の保存についての詳細な説明

序文

フロントエンド分野では、ほとんどの作業が UI で行われるため、メモリ管理は最も見落とされやすい部分です。メモリを理解していないと、多くの問題の本質が見えず、より適切なコードを書くのが難しくなります。今回は、メモリの世界に皆さんをお連れします。

JSマジックナンバー

ケース1:金額の計算と振替

18.9 * 100
=1889.99999999999998

ケース2: 数学的法則の違反

0.1 + 0.2 === 0.3
// 間違い

(関数 (a, b, c) {
    a + b + c === a + ( b + c ) を返す
})(0.1, 0.2, 0.3)
// 間違い

ケース3: 無限ループ加算

(関数 (数値) {
    while(true) {
        (++num % 13 === 0)の場合{
            数値を返す
        }
    }
})(2 ** 53)

ケース4: JSON.parse

JSON.parse('{"a":180143985094813214124}')
//{a: 180143985094813220000}

上記の 4 つのケースから、コンピューターでの数値計算はしばしば「驚き」をもたらすことがわかります。これらの予期しない結果を防ぐために、まず JavaScript で数値がどのように保存されるかを理解する必要があります。

数値の保存

コンピュータはデータを 2 進数で保存するため、数字も対応する 2 進数 (000 または 111 のさまざまな組み合わせ) に変換する必要があります。

バイナリ変換方法

数値をバイナリに変換する方法を説明する例を以下に示します。

10進数106.6953125106.6953125106.6953125を2進数に変換する

小数変換が発生した場合は、整数部分と小数部分を別々に処理する必要があります。整数 106106106 を商が 000 になるまで 222 で割り、各割り算の余り結果を 222 で取ります。

106 / 2 = 53 ... 0
53 / 2 = 26 ... 1
26 / 2 = 13 ... 0
13 / 2 = 6 ... 1
6 / 2 = 3 ... 0
3 / 2 = 1 ... 1
1 / 2 = 0 ... 1
結果は右から左に並べた余り1101010です

小数 0.69531250.69531250.6953125 を 222 倍して小数点がなくなるまで繰り返し、各乗算後の整数結果を数えます。

0.6953125 × 2 = 1.390625 ... 1
0.390625 × 2 = 0.78125 ... 0
0.78125 × 2 = 1.5625 ... 1
0.5625×2 = 1.125…1
0.125 × 2 = 0.25 ... 0
0.25 × 2 = 0.5 ... 0
0.5 × 2 = 1 ... 1
結果は左から右に並べられた整数の数字1011001である。

計算された 000 111 シーケンスを組み合わせると、変換されたバイナリ 1101010.10110011101010.10110011101010.1011001 が得られ、これは科学的記数法で表現すると 1.1010101011001∗261.1010101011001*2^61.1010101011001∗26 となります。バイナリが計算されたので、それをコンピューターに保存する必要があります。Javascript では、整数と小数は区別されず、数値は倍精度浮動小数点数の要件に従って均一に保存されます。主に次の規則が含まれます。

  • 倍精度浮動小数点数を格納するには、8バイト(64ビット)8バイト(64ビット)8バイト(64ビット)を使用します。
  • 科学的記数法で小数点で表されるデータを保存します
  • 最初のビットは符号を示し、最後の 111111 ビットは指数を示します。指数はビット充填操作によって計算されます。つまり、指数ビットに 102310231023 を直接加算します。
  • 残りの525252桁は小数点を表します。525252桁を超える部分は000に切り捨てられ、111に切り上げられます。

指数の 11 ビットには符号ビットが含まれていないため、正の指数と負の指数の効果を実現するために指数オフセットが導入されます。

図は以下のとおりです。

変換した2進数を規則に従ってメモリに格納します。まず、106.6953125106.6953125106.6953125は正の数なので、符号ビットは111になります。000は正の符号を表し、111は負の符号を表します(図では000と表示されていますが、これはタイプミスです)。

2進数 1.1010101011001∗261.1010101011001*2^61.1010101011001∗26 指数は 666 (ここでオフセット 1023 を追加する必要があります) で、2進数に変換すると 100000001011000000010110000000101 になります。指数ビットには 2 の補数を配置する必要があり、2 の補数の計算規則は次のとおりです。

  • 正の数の補数はそれ自体
  • 負の数の補数は、元のコードに基づいて、符号ビットは変更されず、残りのビットは反転され、最終的に +1 になります。(つまり、1 の補数に基づいて +1 になります)
[+1] = [00000001] オリジナル = [00000001] 逆 [-1] = [10000001] オリジナル = [11111110] 逆

画像インデックスを埋める必要があります

仮数部は10進数から変換された2進数で直接埋めることができます

数字は最終的にこの形式でコンピュータに保存されます

なぜ 0.1 + 0.2 !== 0.3 なのでしょうか?

デジタルストレージの原理を理解した後、なぜ0.1+0.2!==0.30.1 + 0.2 !== 0.30.1+0.2!==0.3なのかを分析してみましょう。

まず、0.10.10.1 0.20.20.2 0.30.30.3をそれぞれ2進数に変換します。

0.1 × 2 = 0.2 ... 0
0.2 × 2 = 0.4 ... 0
0.4 × 2 = 0.8 ... 0
0.8 × 2 = 1.6 ... 1
0.6 × 2 = 1.2 ... 1
0.2 × 2 = 0.4 ... 0
0.4 × 2 = 0.8 ... 0
0.8 × 2 = 1.6 ... 1
0.6 × 2 = 1.2 ... 1
得られた整数の桁は左から右に 000110011... のように並べられます。

0.1→0.00011(0011)∞

0.2 × 2 = 0.4 ... 0
0.4 × 2 = 0.8 ... 0
0.8 × 2 = 1.6 ... 1
0.6 × 2 = 1.2 ... 1
0.2 × 2 = 0.4 ... 0
0.4 × 2 = 0.8 ... 0
0.8 × 2 = 1.6 ... 1
0.6 × 2 = 1.2 ... 1
0.2 × 2 = 0.4 ... 0
得られた整数の桁は左から右に 001100110... のように並べられます。

0.2→0.00110(0110)∞

0.3 × 2 = 0.6 ... 0
0.6 × 2 = 1.2 ... 1
0.2 × 2 = 0.4 ... 0
0.4 × 2 = 0.8 ... 0
0.8 × 2 = 1.6 ... 1
0.6 × 2 = 1.2 ... 1
0.2 × 2 = 0.4 ... 0
0.4 × 2 = 0.8 ... 0
0.8 × 2 = 1.6 ... 1
得られた整数ビットは左から右に 010011001... と並べられます。

0.3→0.01001(1001)∞

科学的記法は次のように統一的に表現される。

0.1→0.00011(0011)∞→1.(1001)∞∗2 −4

0.2→0.00110(0110)∞→1.(1001)∞∗2 −3

0.3→0.01001(1001)∞→1.(0011)∞∗2 −2

倍精度浮動小数点数はコンピュータに保存されます。末尾の赤は、バイナリが仮数部を超えていることを示しており、切り上げる必要があることを意味します。

64ビット倍精度保存後のバイナリ表現は次のようになります。
0.1→0−01111111011−(1001) 12 1010

0.2→0−01111111100−(1001) 12 1010

0.3→0−01111111101−(0011) 12 0011

この時点で、0.1+0.20.1 + 0.20.1+0.20.1+0.2は0.30.30.3と等しくありません。

これが、コンピューターでの数値計算がしばしば「驚き」をもたらす理由です。

要約する

JS のメモリと変数の保存に関するこの記事はこれで終わりです。JS のメモリと変数の保存に関するより関連性の高いコンテンツについては、123WORDPRESS.COM の以前の記事を検索するか、以下の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。

以下もご興味があるかもしれません:
  • js 変数、スコープ、メモリの詳細な説明
  • JavaScript の変数、スコープ、メモリの問題について簡単に説明します
  • JavaScript 変数、スコープ、メモリ
  • JavaScript における変数、スコープ、メモリの問題
  • JavaScript 変数のスコープとメモリの問題の分析例

<<:  MySQL 8.0 以降の一般的なコマンドの詳細な説明

>>:  Tomcat プロセスの CPU 使用率が高い場合のトラブルシューティング記録を記録する

推薦する

Linuxターミナルでの一般的なMySQL操作コマンドの詳細な説明

仕える: # chkconfig --list すべてのシステム サービスを一覧表示します # ch...

純粋な CSS で DIV サスペンションを実装するサンプル コード (固定位置)

DIV フローティング効果 (固定位置) は CSS のみで実装されており、IE8、360、Fir...

Linux の MySQL でリモート接続を承認する方法

注意: 他のマシン (IP) は、承認なしではクライアント経由で MySQL データベースに接続でき...

HTML の著作権記号のフォント選択問題 (著作権記号をより美しくする方法)

1. 問題を発見する&copy; は HTML の著作権記号ですが、間違ったフォントを選択す...

MySQL ツリー構造テーブルの設計と最適化に関する簡単な説明

序文多くの管理・オフィスシステムでは、ツリー構造がいたるところで見られます。たとえば、「部門」や「機...

Linux で MySQL データベースのインポートおよびエクスポート コマンドを実装する方法

1. mysqldump コマンドを使用してデータベースをエクスポートします (このコマンドのパスで...

Ubuntu 19.10 で ssh サービスを有効にする (詳細なプロセス)

Ubuntuでsshを開くのに1時間以上かかりました。主な原因は、最初に読んだチュートリアルの手順...

Bootstrap 3.0 の特殊効果の学習ノート(表示と非表示、フローティングの除去、閉じるボタンなど)

この記事の主な内容は次のとおりです。 1. 閉じるボタン2.キャレット3. フローティングを素早く設...

JSはビデオの再生速度を制御するための簡単なサンプルコードを実装します

導入以前、ある問題に気づきました。学習ビデオを視聴しているとき、動きが遅すぎる、先生が黒板に書くのに...

MySQLログシステムの詳細情報共有

大規模なシステムに取り組んだことがある人なら誰でも、ログの役割を過小評価してはならないことを知ってい...

MySQLテーブル構造を変更するコマンドを表示する

簡単な説明エディターはデータベースのエンコードが間違っているために問題に遭遇することが多く、これは頭...

Vue は小数点付きの星評価を実装します

この記事では、小数点付きの星評価を実装するためのVueの具体的なコードを参考までに共有します。具体的...

ラベルタグを使用してテキストをクリックしてラジオボタンを選択します

<label> タグは、入力要素のラベル (タグ) を定義します。ラベル要素はユーザーに...

nginxを使用してドメイン名ベースの仮想ホストを構成する

1. 仮想ホストとは何ですか?仮想ホストは、特殊なテクノロジーを使用して、実行中のサーバーを論理的に...

VS2019 が mysql8.0 データベースに接続する方法 (画像とテキスト付き)

1. まず、VS2019とMySQLデータベースを準備します。どちらも公式サイトからダウンロードで...