JSに限ったことではないですが、JSでも例にもれず浮動小数点演算の誤差があるようです。
普段あまり小さいな小数点気にしてないですが、仮想通貨系のプログラム書いててハマりました(桁が大きいので)。
16桁以上の大きい数字でも誤差があるようです。felisさんからコメントいただきました。
対応
- 整数に直して計算する
- ライブラリを使う
bignumber.jsというのを使うとよいらしいので使ってみます。他にもあるみたいです。
必要なライブラリのインストール
作業場所で、
npm install bignumber.s
実装
とりあえず、index.jsってファイルに以下を記述。
var BigNumber = require('bignumber.js');
var ans1 = 1.1 * (10**18)
var ans2 = BigNumber(1.1).times(10**18);
console.log("ans1:"+ans1);
console.log("ans2:"+ans2);
実行
node index.js
結果
bignumber.jsで処理していない方は値がおかしい
ans1:1100000000000000100
ans2:1100000000000000000
結論
小数点自体の処理、結果が少数点になることが予想される処理はbignumber.jsを利用する。
追記
16桁以上でもおかしくなると教えてもらったので検証。
var BigNumber = require('bignumber.js');
//動作1
if((1e16+ 1) === 1e16) {
console.log("同じ1");
} else {
console.log("同じじゃない1");
}
//表示してみる
console.log("1e16+1="+(1e16+1));
//動作2
if(new BigNumber(1e16 + 1) === 1e16){
console.log("同じ2");
} else {
console.log("同じじゃない2")
}
実行結果。
計算上同じに評価されるだけじゃなく、そもそも1がどっか消えてしまってます。
同じ1
1e16+1=10000000000000000
同じじゃない2
1e15で検証したら、それは問題なかったです。1e16+2だと同じにはなりません。
仮想通貨の実装では内部的にはuintとして扱い、Ethereumの仕様乗、e18以上をよく扱うので大変参考になりました。JSが正しく計算できるのは2の53乗まで。web3.jsは内部的にbignumber.jsを利用してるようですが、素で計算するときは注意が必要です。