普段良く使うJavascriptはブラウザで簡易にプログラミングできて便利なんですが、小数点の計算はとにかく苦手なようです。
プログラミングをして計算をする時に、厳密な答えが返ってくるのが当たり前だし、人間が普通に計算してすぐに答えがでるような簡易なものも、javascriptは誤差が出ることが分かったので、その調査内容をブログに書いておきます。
0.3-0.1=0.19999999999999998...????
ウソでしょ!!!というような計算結果に驚愕ですが、0.3から0.1を引いたら、誰でも0.2だという事はわかりますが、javascriptでは" 0.19999999999999998"という答えが返ってきます。
1÷3のような割り切れない計算であれば、わかるのですが、小数点16桁の誤差というのも気持ち悪い感じです。
これは、ブラウザのjavascriptコンソールでも、スクリプトファイルでも、どこでも発生してしまいます。
そして、"0.2-0.1"とすると、問題なく"0.1"という答えが返ってくるので、さらに混乱してきます。
計算誤差マップ
とりあえず、誤差が発生する法則がつかめないので、0.1刻みでのマトリクス表をjavascriptで表示させてみました。
※このソースコードは下部に掲載しておきます。
この表をみると、結構な割合で、誤差が発生しているのがわかります。
確認の為に言っておきますが、この表は縦軸から横軸を引き算する表になっていて、割り算しているわけではありません。
そして、何度もリロードして確認したり、別のブラウザで表示してみたところ、全ての状態で全く同じ結果が表示されたので、どうやらこの誤差はjavascriptの正式な計算結果であることもわかりました。
誤差を出さ無いための解決方法
javascriptでの小数点計算は誤差がどうしてもでてしまうという事を前提に、誤差の出ない計算方法はないかと調べてみると、全ての値を整数値で計算すれば、誤差は発生しなくなります。
"0.3-0.1"という計算式を、"(0.3 * 10 - 0.1 * 10) / 10"という風に、小数点をカバーする桁数の整数値に変換して、最後に割り算にて小数点ずらしをしてあげるという事で、小数点誤差を回避するしかなさそうです。
こんなめんどくさい事をさせるプログラム言語も珍しいのではないでしょうか。
計算式を回避できるようなスニペットがあればいいんですが、"
decimal.js"というライブラリがあるようですね。
御大層なソースコードにドン引きですが、余裕があれば、こういうのを使うてもありますが、現時点では、プログラムコーディングにおいて、自己修復するしかなさそうです。
また、割り切ってできるのであれば、小数点第何位かを決めて四捨五入するという荒業も悪くないかもしれません。
誤差マトリクスのソースコード
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8"/>
<title>javascript [dataset]</title>
<script type="text/javascript" src="error.js"></script>
</head>
<style>
table{
border-collapse:collapse;
transform:scale(0.7);
transform-origin:left top;
}
td,th{
border:1px solid #ccc;
padding:2px 4px;
font-size:10px;
white-space:pre;
}
th{
background-color:#EEE;
font-weight:normal;
}
</style>
<body>
<h1>JavaScript - Decimal - Error</h1>
<table class="error"></table>
</body>
</html>
window.addEventListener("load",function(){
var table = document.querySelector(".error");
for(var i=0; i<=30; i++){
var x = i / 10;
var tr = document.createElement("tr");
var th_c = document.createElement("th");
if(i===0){
th_c.textContent = "x(↓) - y(→)";
}
else{
th_c.textContent = x;
}
tr.appendChild(th_c);
for(var j=1; j<=10; j++){
var y = j / 10;
calc = x - y;
if(i===0){
var cell = document.createElement("th");
cell.textContent = y;
}
else{
var cell = document.createElement("td");
cell.textContent = calc;
}
tr.appendChild(cell);
}
table.appendChild(tr);
}
});
0 件のコメント:
コメントを投稿