JSでは浮動小数点はどう分かりますか?
4202 ワード
本文は雲+コミュニティから発表します.
通常のJavaScriptの開発において、浮動小数点演算の精度誤差に遭遇したことがあると思います.例えば、
Numberの格納基準
JavaScript Numberは、IEEE 754が定義する64ビットのデュアル精度浮動小数点型を採用して表しています.具体的なバイト割りは、まずウィキペディアから引いた図を見てもいいです.
上の図から見ると、高さから低さまで、64ビットは3つのセグメントに分けられ、それぞれ: sign:シンボルビット、1ビットを占めます. exponent:指数ビット、11位を占めます. fraction:有効数字位は52桁を占めています. 指数は11桁で、範囲は0から2047までです.指数ビットe=0またはe=2017の場合、有効数字ビットfが0かどうかによって、異なる特殊な意味を持っています.具体的には下表を参照してください.
一般的なnormal numberについては、指数が負の場合を表しやすいように、指数ビット値の大きさは−1023のオフセット量とされている.1つの0数字ではないにとって、そのバイナリの科学的な数え方の中の第一位の有効な数字は固定して1です.このように、ダブル精度の浮動小数点型の数字の値は
subnormal numberについては、0に近い数を表すために使用されても良いが、その特殊なところは、有効な数字ビットの前に0が補足されており、1ではなく、指数がオフセットであることは、−1022である.
Numberオブジェクトの幾つかの属性値
Numberがどのように記憶されているかを知ると、Numberオブジェクトの属性はどのように値を取るかが明らかになります.
Number.MAX_VALE:表すことができる最大の数は、明らかにeとfが一番大きい時に表すことができる数を取っています.
Number.MIN_VALE:表示できる最小の正数は、最小のsubnormal numberで表します.e=0の場合、fの最後のビットは1、その他は0の場合は最小、値は
Number.EPSILON:1とNumberが表すことができる1より小さい浮動小数点の差を表します.値は
Number.MAX SAFEINTEGER:JavaScriptの中で最大の安全整数を表します.連続的かつ正確に表現できる整数は、2^54のような安全な整数ではなく、2^54+ 1の2つの数の表現とは全く同じであり、e=1077、f=0である.
Number.MINSAFENTEGER:JavaScriptの中で最小の安全整数を表し、
なぜ0.1+0.2が0.3に等しくないですか?
上の図では、得られた和に対して、「ゼロ捨一入」が52桁の有効小数を保持することが最終的な値です.0.01001100…110100(53位は1ですので、前に進みました.1)は、下記のコードで示しています.この値は、上記の0.3の最終的なバイナリ表現の値とは明らかに違っています.即ち、0.1+0.2が0.3に等しくない根本的な原因が説明されています.注:印刷された長さは54で、52ビットの有効小数があるため、前は'0.01'で、長さは4で、最後に末尾の2つの0を抜きます.最後に印刷された長さは52+4-2=54です.
js浮動小数点演算精度が失われた問題については、シーンによって解決策が異なります.1、浮動小数点数の結果を示すだけであれば、NumberオブジェクトのtoFixedとParseFloat方法を借りることができます.以下のコードセグメントにおいて、fixedパラメータは数桁の小数を保留すると表しています.実際のシーンによって精度を調整できます.https://en.wikipedia.org/wiki... https://en.wikipedia.org/wiki... https://en.wikipedia.org/wiki... https://en.wikipedia.org/wiki... この文はすでに作者の許可でテンセント雲+コミュニティが発表しました.
通常のJavaScriptの開発において、浮動小数点演算の精度誤差に遭遇したことがあると思います.例えば、
console.log(0.1+0.2===0.3)// false
.JavaScriptでは、すべての数字は整数と小数を含むNumber
タイプで表されています.本明細書では、Number
のバイナリ記憶規格を紹介することにより、浮動小数点演算精度の問題を理解し、Number
オブジェクトのMAX_VALUE
などの属性値がどのように値を取るかを理解し、最後に一般的な浮動小数点精度演算ソリューションを紹介する.Numberの格納基準
JavaScript Numberは、IEEE 754が定義する64ビットのデュアル精度浮動小数点型を採用して表しています.具体的なバイト割りは、まずウィキペディアから引いた図を見てもいいです.
上の図から見ると、高さから低さまで、64ビットは3つのセグメントに分けられ、それぞれ:
一般的なnormal numberについては、指数が負の場合を表しやすいように、指数ビット値の大きさは−1023のオフセット量とされている.1つの0数字ではないにとって、そのバイナリの科学的な数え方の中の第一位の有効な数字は固定して1です.このように、ダブル精度の浮動小数点型の数字の値は
subnormal numberについては、0に近い数を表すために使用されても良いが、その特殊なところは、有効な数字ビットの前に0が補足されており、1ではなく、指数がオフセットであることは、−1022である.
Numberオブジェクトの幾つかの属性値
Numberがどのように記憶されているかを知ると、Numberオブジェクトの属性はどのように値を取るかが明らかになります.
Number.MAX_VALE:表すことができる最大の数は、明らかにeとfが一番大きい時に表すことができる数を取っています.
Number.MIN_VALE:表示できる最小の正数は、最小のsubnormal numberで表します.e=0の場合、fの最後のビットは1、その他は0の場合は最小、値は
Number.EPSILON:1とNumberが表すことができる1より小さい浮動小数点の差を表します.値は
Number.MAX SAFEINTEGER:JavaScriptの中で最大の安全整数を表します.連続的かつ正確に表現できる整数は、2^54のような安全な整数ではなく、2^54+ 1の2つの数の表現とは全く同じであり、e=1077、f=0である.
Math.pow(2,54)===Math.pow(2,54)+1// true
.整数が二進法に変換された後、小数点以下では数字がありません.二進法の科学計数法で表した場合、小数点以下では最大52桁を保持し、前の1つを加えて53桁の数字があります.だから、一つの数字が二進法に変換された場合、桁が53桁を超えると、必ず最後の部分を切り捨てます.つまり、正確に表現できなくなります.つまり、安全でない整数です.したがって、最小のカットされる整数は100…001=2^531+1(中間52個の0)です.この数をXにすれば、Xより小さい整数が正確に表示され、連続という条件が加わるので、X-1は私たちが求める答えではなく、X-2が必要です.Number.MAX_SAFE_INTEGER
最終値はNumber.MINSAFENTEGER:JavaScriptの中で最小の安全整数を表し、
Number.MAX_SAFE_INTEGER
に対してマイナス値を取ればいいです.値は-9007199254740991です.なぜ0.1+0.2が0.3に等しくないですか?
console.log(0.1+0.2===0.3)// false
のこの問題を見てみますと、数字0.1がバイナリに変換されるのは0.000100011…つまり1.11001 2^-4です.53位は1で、10進数の4捨5入に似ているので、バイナリは「ゼロ捨1入」です.したがって、0.1の最終バイナリ科学カウント法は1.10011…1010 2^-4であり、即ちバイナリ数値サイズは実際に0.0001001101…10011010であると表しています.下のコードはこの値を検証しました.var a = 0.1;console.log(a.toString(2)); //0.0001100110011001100110011001100110011001100110011001101
同じ10進数の数字0.2をバイナリに変換する最終値は1.1001001…1010 2^-3で0.00110011...100111010です.十進数0.3変換ビットバイナリの最終値は1.00110011…0011 2^-2です.var b = 0.2;console.log(b.toString(2)); //0.001100110011001100110011001100110011001100110011001101var c = 0.3;console.log(c.toString(2)); //0.010011001100110011001100110011001100110011001100110011
したがって、0.1+0.2の値は、上の0.1と0.2に対応するバイナリ値の加算となり、下の図のようになります.上の図では、得られた和に対して、「ゼロ捨一入」が52桁の有効小数を保持することが最終的な値です.0.01001100…110100(53位は1ですので、前に進みました.1)は、下記のコードで示しています.この値は、上記の0.3の最終的なバイナリ表現の値とは明らかに違っています.即ち、0.1+0.2が0.3に等しくない根本的な原因が説明されています.注:印刷された長さは54で、52ビットの有効小数があるため、前は'0.01'で、長さは4で、最後に末尾の2つの0を抜きます.最後に印刷された長さは52+4-2=54です.
var d = 0.1 + 0.2;console.log(d.toString(2)); //0.0100110011001100110011001100110011001100110011001101console.log(d.toString(2).length); // 54
浮動小数点精度演算ソリューションjs浮動小数点演算精度が失われた問題については、シーンによって解決策が異なります.1、浮動小数点数の結果を示すだけであれば、NumberオブジェクトのtoFixedとParseFloat方法を借りることができます.以下のコードセグメントにおいて、fixedパラメータは数桁の小数を保留すると表しています.実際のシーンによって精度を調整できます.
function formatNum(num, fixed = 10) { return parseFloat(a.toFixed(fixed))}var a = 0.1 + 0.2;console.log(formatNum(a)); //0.3
2、浮動小数点の加減乗除などの演算が必要であれば、Number.MAXSAFEINTEGERの範囲より小さい整数は正確に表現できるので、まず小数を整数に変換して演算結果を得てから対応する小数に変換することができます.例えば、2つの浮動小数点の加算です. function add(num1, num2) { var decimalLen1 = (num1.toString().split('.')[1] || '').length; // var decimalLen2 = (num2.toString().split('.')[1] || '').length; // var baseNum = Math.pow(10, Math.max(decimalLen1, decimalLen2)); return (num1 * baseNum + num2 * baseNum) / baseNum;}console.log(add(0.1 , 0.2)); //0.3
参考資料