■補講 JavaScriptで任意精度計算
本書はHTML5 APIの入門書なので、この補習講義は少しそれたものになります。JavaScriptはMathオブジェクトを使って様々な計算を行うことができます。また、一般的な四則演算を行うこともできます。
しかし、いずれの演算も精度に限界があります。つまり計算できる桁数に限界があるのです。JavaScriptの演算はdouble型という64bitを使っています。しかし、64bitの中でも符号と指数部、仮数部に分かれており、符号部に1ビット、指数部に11ビット、仮数部に52ビットが割り当てられています。以下のページにC言語でのdouble型の説明があるので参照してみるとよいでしょう。
●浮動小数点と誤差
http://www.cc.kyoto-su.ac.jp/~yamada/programming/float.html
JavaScriptで小数値の演算を行うと誤差により思わぬ結果になることがあります。もっともシンプルなところでは0.1を10回足しても1にならないというのがあります。
そんな誤差が出る演算は困る(給与計算が違ってくる、利子計算が違ってくる等々)という人もいるでしょう。このような場合は浮動小数計算を行わないように10倍、1000倍してから演算を行います。通称、ゲタをはかせるという手法です。コンピューターは整数計算では誤差がでませんので、この方法は速度面から見ても有益だと言えるでしょう。
ところが、これとは別に非常に巨大な数値計算を行いたい場合があります。ということで、ようやく本題なのです。メモリが許す限りの演算を行う、つまり「任意精度演算」をJavaScriptでやってみます。
●任意精度計算
http://ja.wikipedia.org/wiki/任意精度演算
とは言え、ここで作成したものは非常にシンプルなものです。桁数はメモリが許す限りOKですが、1桁の加算しかできません。手持ちのマシンでは1000桁までは加算することができました。これはJavaScriptが扱える数値範囲を軽く超えています。1億桁もやってみましたが、ブラウザからの反応がないのでここらへんが限界なのかもしれません。また、Float32Arrayなど仕様するメモリサイズが決まっている配列を使うのもよいかもしれません。
実際のサンプルは以下のようになります。1桁の計算しかできませんが、このような場合の計算方法はWebをさがせば出てきますので興味がある方は挑戦してみるとよいでしょう。
----------------------------------------------------------------------------------------
■サンプルコード
----------------------------------------------------------------------------------------
// メモリが許す限り計算
var data = [];
for(var i=0; i<100; i++){ // 100桁の計算
data[i] = 0;
}
window.addEventListener("load", function(){
// 「値を加算する」ボタンがクリックされた時の処理
document.getElementById("addButton").addEventListener("click", function(){
var n = parseInt(document.getElementById("num").value);
add(n);
display();
}, true);
// 計算結果(配列内容)をページ上に表示
function display(){
var txt = "";
for(var i=data.length-1; i>=0; i--){ // 桁数の数だけ表示
txt = txt + String.fromCharCode(0x30 + data[i])+" ";
}
document.getElementById("result").innerHTML = txt;
}
display();
}, true);
// 加算
function add(n){
var ptr = 0; // 最初の桁から加算していく(つまり最初の配列要素から加算)
var len = data.length;
while(true){
data[ptr] = data[ptr] + n; // 加算する
if (data[ptr] < 10){ return; }// 10以下なら処理が終わったので関数から抜ける
data[ptr] = data[ptr] - 10; // 1桁にする
n = 1; // 次の桁に加算する値。これは必ず1になる
ptr = ptr + 1; // 加算する次の桁
if (ptr > len){
data[ptr] = 1;
return;
}
}
}