はじめに
小数点を含んだ演算では、演算結果に誤差が生じ、意図した結果が得られないが場合ある。
これは、内部的には数値を (10進数ではなく) 2進数で演算しているため生じた誤差。
本記事は小数点を含んだ演算で厳密に結果を得る必要がある、あるいは値を比較する場合の誤差対処法について紹介する。
対処法
方法1
1. 値をいったん整数にしてから演算する
2. 1.の結果を再び小数に戻す
// 演算結果に誤差が生じる
console.log(0.2 * 3);
// 桁数を上げて演算して、結果を再び小数に戻す
console.log(((0.2 * 10) * 3) / 10);
実行結果
0.6000000000000001
0.6
数値の組み合わせによっては、これでも正しく動作しない場合がある。
下記の例では積算 / 除算 (100) する際に、すでに小数誤差が発生してしまうため。
let x = 0.14 * 100;
let y = 0.28 * 100;
console.log(x);
console.log(y);
// 100を積算 / 除算する際に、すでに小数誤差が発生してしまった
console.log((x + y) / 100);
実行結果
14.000000000000002
28.000000000000004
0.4200000000000001
これを解消するためには、下記のようにMath.round関数を使用して小数点以下の値を四捨五入するなどを行う必要がある。
let x = 0.14 * 100;
let y = 0.28 * 100;
console.log(Math.round(x + y) / 100);
実行結果
0.42
方法2
ライブラリーを利用する
本記事はdecimal.jsライブラリーを例として挙げる。
decimal.jsでは、内部的な演算にも10進数を用いていることから2進数演算によって生じてしまう丸め誤差を防ぐため、より確実に小数点の演算を解決したい場合は、ライブラリーを使用することをおすすめする。
<!-- CDN経由でライブラリインポート、 一例としてCDNを使用している -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/decimal.js/10.4.2/decimal.min.js"></script>
// リテラルの段階で誤差が生じてしまう可能性があるため、コンストラクターに浮動小数点をそのまま渡すべきではない
let d1 = new Decimal('0.14');
let d2 = new Decimal('0.28');
// new Decimal()で生成したdecimal値をaddメソッドで加算する
// 演算結果はdecimal値なので、toNumberメソッドを呼び出して本来のnumber型に戻す (文字列化を行いたい場合は、toStringメソッドも利用できる)
console.log(d1.add(d2).toNumber());
実行結果
0.42
以下が、一般的によく使われる四則演算、比較メソッドの一覧。他にも色々と用意されているので詳細な機能はdecimal.jsの公式サイトを参照。
メソッド | 概要 |
---|---|
add | 加算 |
sub | 減算 |
mul | 乗算 |
div | 除算 |
eq | 等しい |
gt | より大きい |
gte | 以上 |
lt | 未満 |
lte | 以下 |