BETA

JavaScriptでの小数計算の誤差をどうにかした

投稿日:2020-05-26
最終更新:2020-05-26

はじめに

JavaScript では小数計算がバグる仕様です。

console.log(1 - 0.9); // 0.09999999999999998  

IEEE 754という標準に則った JavaScript の仕様により、小数の計算で誤差が生まれてしまうケースがある。

これをどうにかする方法はいくつかあるが、このサイトのものがとてもわかりやすく良かった。

今回はこれを新しめな書き方に改良した。

プログラム

class ExMath {  
  /**  
   * 与えられた値の小数点以下の桁数を返す  
   * @param {number} value  
   */  
  getDecimalLength(value) {  
    return value.toString().split('.')[1]?.length ?? 0;  
  }  

  /**  
   * 減算処理  
   * @param {number[]} values  
   */  
  subtract(...values) {  
    // values を整数値に変換して減算  
    // その後、小数点の桁数分だけ小数点位置を戻す  

    const max = Math.max(  
      ...values.map((value) => this.getDecimalLength(value)),  
    );  
    const k = 10 ** max;  
    return (  
      values  
        .map((value) => this.multiply(value, k))  
        .reduce((acc, cur) => acc - cur) / k  
    );  
  }  

  /**  
   * 乗算処理  
   * @param {number[]} values  
   */  
  multiply(...values) {  
    // values から小数点を取り除き、整数値のみで乗算を行う  
    // その後、小数点の桁数 N の数だけ 10^N で除算する  

    const intValues = values.map((value) =>  
      Number(value.toString().replace('.', '')),  
    );  
    const decimalLength = values  
      .map((value) => this.getDecimalLength(value))  
      .reduce((acc, cur) => acc + cur, 0);  
    return intValues.reduce((acc, cur) => acc * cur) / 10 ** decimalLength;  
  }  
}  

// 減算テスト  
console.log(67 - 66.9); // => NG 0.09999999999999432  
console.log(new ExMath().subtract(67, 66.9)); // => OK 0.1  
// 乗算テスト  
console.log(66.9 * 100); // => NG 6690.000000000001  
console.log(new ExMath().multiply(66.9, 100)); // => OK 6690  

こだわりポイント

型変換

number + ''stringにしたりとか+stringnumberにしたりとかは個人的に嫌いなので使わなかった。

ExMath

全体をclassにしてカプセル化した。

カプセル化ってこういうことであってるよね?

getDecimalLength

Optional ChainingNullish Coalescing を使って短くした。

subtractmultiply

スプレッド構文を使って可変長引数対応をした。

Math.powの代わりに**(名前がわからない)を使った。

おわりに

ライブラリ(が使えない OR を使いたくない)状況ではこういった自作したものが役に立つかもしれない。

技術ブログをはじめよう Qrunch(クランチ)は、プログラマの技術アプトプットに特化したブログサービスです
駆け出しエンジニアからエキスパートまで全ての方々のアウトプットを歓迎しております!
or 外部アカウントで 登録 / ログイン する
クランチについてもっと詳しく

この記事が掲載されているブログ

Qrunchクローズを受けQiitaへ移行

よく一緒に読まれる記事

0件のコメント

ブログ開設 or ログイン してコメントを送ってみよう