ソフトウェア開発者の日常

こだわりなく書きたいことを書いていきます。

JavaScript:計算誤差の対処

JavaScriptで計算している部分で、想定と異なる結果になる場合がありました。
具体的には、36.3×700=25,410になるはずですが、25,409になります。

ブラウザのデバッガで値を確認しても、36.3と700のまま掛け算をしていますが、25,409になります。
検索すると、JavaScriptIEEE 754という規格に従って実装されているためのようです。
IEEE 754 - Wikipedia

f:id:AJYA:20171212124055j:plain
photo credit: Canadian Pacific Do Your Math! via photopin (license)

整数同士なら誤差がでない

対処方法としては、公開されているJavaScriptライブラリを導入する方法もあるようです。
今回計算しているのは1箇所だけだったので、以下の記事を参考に実装しました。
qiita.com
小数点の以下の桁数を把握して、文字列に変換して整数化、整数同士で計算後、小数点以下の桁数を戻すということをしています。

こちらの記事をほぼそのまま利用できますが、2点変更しました。
1点目は、getDotPosition()の

    // 小数点が存在するか確認
   if(strVal.lastIndexOf('.') === -1){

の部分が、小数点が存在しない場合にしか処理しないようになっていて、コメントと合っていません。
lastIndexOf()メソッドは、検索する文字列が見つからない場合は、-1を戻すためです。
developer.mozilla.org>
コメントのとおりに動作するように、

    // 小数点が存在するか確認
   if(strVal.lastIndexOf('.') !== -1){

に変更しました。

2点目は、calcSubtract()の

    // 10^N の値を計算
    var power = Math.pow(10,max);

    // 整数値で引き算した後に10^Nで割る
    return (intValue1-intValue2) / power;

の部分が引き算の例だったので、掛け算用に

    // 10^N の値を計算
    if (max == 1) {
        max = max + 1;
    } else {
        max = max * max;
    }
    var power = Math.pow(10,max);

    // 整数値で掛け算した後に10^Nで割る
    return (intValue1*intValue2) / power;

に変更しました。
maxが1の場合は2、それ以外はmax同士を掛けています。
小数点以下の位が多いほうに合わせて両方の値に同じだけ掛けているので、整数値で計算します。
計算後、掛けた分で割らなければいけないので、maxを操作しています。


JavaScriptで計算誤差に気がついたのは初めてです。
小数点以下を伴う計算であればいつでも起きる可能性があるので、気にしておかなければならない点です。