4

浮動小数点数で数学演算を行うとき、JavaScript で何が起こっているのか正確には理解できません。私は小数を使用することを非常に恐れており、可能な限り小数を使用しないようにしています。ただし、IEEE 754 規格に関して舞台裏で何が起こっているかを知っていれば、何が起こるかを予測できます。予測可能性があれば、私はより自信を持ち、恐れることがなくなります。

誰かが私に簡単な説明をしてくれませんか ( IEEE 754 標準がどのように機能し、どのようにこの副作用を与えるかについて、整数のバイナリ表現を説明するのと同じくらい簡単0.1 + 0.2 != 0.3です: ?

本当にありがとう!:)

4

4 に答える 4

10

0.1 のような小数は、基数 2 ではきれいに表現できません。

10 進数の 0.1 を基数 2 で表現したいとしましょう。1/10 に等しいことがわかっています。基数 2 で 1 を 10 で割った結果は、100.000110011001100...進数の繰り返しシーケンスを使用します。

したがって、10 進数では 0.1 のような数値をきれいに表現するのは実際には非常に簡単ですが、基数 2 では 10 分の 1 に基づく有理数を正確に表現することはできません。格納できるビット数を使用することによってのみ近似できます。

簡単にするために、その数値の最初の、たとえば 8 つの有効な 2 進数を再現するのに十分な記憶域しかないとしましょう。格納される数字は 11001100 (指数は 11) になります。これは、基数 2 で 0.000110011 に変換されます。これは、10 進数では 0.099609375 であり、0.1 ではありません。これは、0.1 を基本値を 8 ビット (符号ビットを含まない) で格納する理論上の浮動小数点変数に変換した場合に発生するエラーの量です。

浮動小数点変数が値を格納する方法

IEEE 754 の標準では、符号と 2 進指数を使用して、実数を 2 進でエンコードする方法が指定されています。指数はバイナリドメインで適用されます。つまり、バイナリに変換する前に小数点をシフトせず、後でシフトします。

IEEE 浮動小数点数にはさまざまなサイズがあり、それぞれが基数に使用される 2 進数の桁数と指数に使用される桁数を指定します。

が表示0.1 + 0.2 != 0.3されているのは、実際には 0.1 または 0.2 に対して計算を実行しているのではなく、これらの数値を浮動小数点 2 進数で特定の精度に近似して計算しているためです。結果を 10 進数に戻すと、このエラーのため、結果は正確に 0.3 になりません。さらに、結果はバイナリ近似の 0.3 にも等しくなりません。エラーの実際の量は、浮動小数点値のサイズ、つまり使用された精度のビット数によって異なります。

丸めが役立つ場合もありますが、この場合はそうではありません

場合によっては、2 進数への変換での桁落ちによる計算エラーは、2 進数からの再変換中に値が四捨五入されるほど小さいため、違いに気付くことさえありません。働きました。

IEEE 浮動小数点には、この丸めの実行方法に関する特定の規則があります。

ただし、0.1 + 0.2 対 0.3 では、丸めによってエラーが相殺されません。 0.1 と 0.2 の 2 進近似を加算した結果は、0.3 の 2 進近似とは異なります。

于 2013-07-25T04:47:05.553 に答える
4

単純に 1/3 を 0.333 (または任意の有限数の 3) に変換すると、1/3 + 1/3 + 1/3 != 1 となるのと同じ理由です。0.333 + 0.333 + 0.333 = 0.999、1 ではありません。

たとえば、底が 9 の場合、1/3 は 0.3 9と正確に表すことができ、0.3 9 + 0.3 9 + 0.3 9 = 1.0 9となります。底 9 で正確に表現できるいくつかの数値は、底 10 では正確に表現できないため、必ず、底を丸めることができる数に丸める必要があります。

同様に、0.2 のように、基数 2 では正確に表現できないが、基数 10 では表現できる数値もあります。
0.2 10は 0.0011001100110011... 2です。これを 0.0011 2
丸めると、0.0011 2 + 0.0011 2 + 0.0011 2 + 0.0011 2 + 0.0011 2 = 0.1111 2であり、1.0000 2ではありません。(0.1111 2は 15/16 です)

コンピューター (少なくとも私たちが使用するコンピューター) は 2 進数で演算を行うため、影響を受けます。

使用する桁数が多いほど、結果の精度が向上することに注意してください。(0.33333333 10 + 0.33333333 10 + 0.33333333 10 = 0.99999999 10 、0.999 10よりも正解に近い)
このため、丸めによる誤差は通常非常に小さいです。Adoubleは約 15 桁の 10 進数を格納するため、相対誤差は約 10 -15 (より正確には 2 -52 ) です。

エラーは小さいため、次の場合を除き、通常は違いはありません。

  • プログラムに非常に高い精度が必要な場合、または
  • 小数点以下の桁数を多くして表示する (0.99999999999999995622 のような数値が表示される場合があります)。
  • 2 つの数値が等しいかどうかを比較します (==またはを使用!=)。

非整数の等価性を比較することは絶対に避けるべきことですが、計算やその他の比較 (<または>) で問題なく使用できます (プログラムが非常に高い精度を必要とする場合を除きます)。

于 2013-07-25T05:32:48.220 に答える
2

JavaScriptでは、私はいつも (Math.abs(.1+.2-.3)<.000001) のようなことをします

私はいつもこのように考えています... .25 ピザ + .25 ピザ != .5 ピザ (ピザを切ると失われます) 笑

于 2013-07-25T04:59:49.603 に答える
1

浮動小数点数の使用に自信が必要な場合は、少なくとも 15 桁の有効数字が有効であることを覚えておいてください。これは、一般的なタスクにはほとんどの場合十分です。

日常業務に必要な有効桁数はさまざまです。たとえば、エンジニアは 3 桁しか使用しない場合もあれば、エコノミストは 5 桁を使用する場合もあり、科学者はそれより多い (または少ない) 場合もあります。したがって、最初に必要な有効桁数を計算します (たとえば、2 兆 3 億 4587 万 6234 ドルを見たいですか、それとも 23 億ドルでいいですか )。有効桁数が 5 桁未満の場合は、少なくとも有効桁数 7 桁で算術を安全に実行し、最後に必要な有効桁数に結果を丸めることができます。

たとえば、有効数字が 3 桁しか必要ない場合:

(0.1 + 0.2).toFixed(3) // 0.300

必要な数より少なくとも 2 桁多く有効数字を常に使用し、最後に必要な数に丸めれば、JavaScript の数値によって生じる小さなエラーに悩まされることはありません。

于 2013-07-25T05:08:26.697 に答える