コンテンツへスキップ

【JavaScript】JavaScriptで小数点以下の計算に誤差が生まれる挙動の対策

  • 未分類

JavaScriptで小数点以下の計算を行うとき、なぜか計算があわないというときがあります。
今回は、その原因と対策方法について紹介したいと思います。

JavaScriptで小数点以下の誤差が生まれるケース

let numA = 0.1
let numB = 0.2

console.log(numA + numB)    // 出力結果:0.30000000000000004

JavaScriptで上記の計算を行った際に答えが「0.3」にならず「0.30000000000000004」となってしまいます。

この問題は一見JavaScriptのバグっぽいんですが、実際は仕様から生まれる問題になっています。

この問題が発生する原因としては、JavaScriptのNumber型がIEEE 754という規格に従っていることにあります。

この規格では、数値を2進数で扱っているのですが小数点以下が発生する10進数の数値を2進数に変換するとき、完全に一致しない場合があるためかなり深いところで誤差が発生してしまっているようです。

さらに踏み込んだ理解については、他の方が追及したブログがたくさんありますので以下のような記事を参照してもらえればと思います。

参考サイト:小数計算の誤差 0.1 + 0.2 が 0.30000000000000004 になる理由 | あぱーブログ

JavaScriptの計算誤差への対策方法

このJavaScriptの計算誤差への対策方法は大きく分けて2つの方法があります。

対策方法1:小数を整数に変換して計算する

今回の計算誤差が発生する原因は、小数点以下が発生する数値であることが原因のため、計算時に小数を整数に変換して計算することで、計算誤差を生まないようにします。

let numA = 0.1
let numB = 0.2

numA = numA*10
numB = numB*10

console.log((numA + numB) / 10)    // 出力結果:0.3

上記のように、少数同士の計算を行う前に整数に変換してから計算を行っています。
最後に10で割ることで本来求めていた計算値を取得することができます。

べき乗を求める

上記の計算式であれば、numAもnumBも小数第一位までの数値のため、双方に10を掛けることで整数へと変換できますが、実際には小数点以下の桁数はバラバラのため掛けるべき10のべき乗を求める必要があります。

let numA = 0.1
let numB = 0.0002

// numAとnumBの小数点以下の文字数を取得
let digitsA = numA.toString().split('.')[1].length
let digitsB = numB.toString().split('.')[1].length

// べき乗
let power = Math.max(digitsA, digitsB)

// 整数に変換
numA = numA * (10**power)
numB = numB * (10**power)

// 最後に掛けたべき乗で割る
console.log((numA + numB) / (10**power))

2つの小数を整数に変換するために利用する10のべき乗を求める方法としては、上記のやり方が一例になります。

簡単に説明すると、numAとnumBの両数値の小数点以下の文字数を取得し、大きい方を採用して変数powerに格納します。

10**powerの計算式で10のべき乗を求められるので、numAとnumBにそれぞれ掛けてあげることで両数値を整数に変換できます。

そして整数同士の計算後に10**powerで割ってあげることで誤差なく答えを求めることができます。

対策方法2:ライブラリを利用する

対策方法の2つ目は、オープンソースで提供されているライブラリがありますのでそれらを利用させてもらいます。

big.js

MikeMcl/big.js

big.jsはgithubで公開されているライブラリの一つです。
導入方法などのマニュアルは公式のUSAGEを見てもらうのが一番いいと思いますので、上記URLから参照してください。

let numA = new Big(0.1)         // 計算の軸となる数値
let numB = 0.2

// 計算の軸となるnumAに対して、numBを加算する
let ans = numA.plus(numB)

console.log(ans.toNumber())    // 出力結果:0.3

big.jsの簡単な使用例は上記のような形です。

① numAの定義として「new Big(0.1)」を格納し、計算となるコンストラクタを作成します。

② そしてnumA.plus(numB)とすることで計算を実行してくれます。

③ 出力結果は上記のように計算誤差がない状態として出力してくれます。

big.jsを利用した計算を行う場合は、通常の計算式とは違い、計算軸となる数値を決めて記述していく必要があるため、複雑な計算を行う際には細かい考慮が必要になるかもしれません。

JavaScriptの計算誤差を解消してくれるライブラリはbig.jsの他にも色々とありますので、他のものも試してみるといいかと思います。

まとめ

というわけで、JavaScriptで小数計算を行った際に発生する計算誤差と対策方法についてご紹介させていただきました。

JavaScriptの仕様上、小数計算を行うことでの誤差が生まれてしまうことは避けられないため、上記の対策を行うか、計算処理だけ他の言語に任せるというやり方もありかもしれません。