JavaScript

(JavaScriptにおける)浮動小数点演算

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を利用してるようですが、素で計算するときは注意が必要です。