Snap!における四則演算のブロックは浮動小数点による計算のため,例えば0.1+0.2と0.3は等しくならない。
また,変数を0から0.1ずつ増やして10回繰り返した場合の値も1とは一致しない。
これらは初等教育などでSnap!を利用する場合に(Scratchでも同様であるが)気をつける必要がある。このページでは,Snap!の四則演算のブロックの計算部分などを,javascriptで固定小数点を扱うために真鍋氏によりGithubで公開されているライブラリーJSDecimalを用いて変更して,四則演算を浮動小数点計算から固定小数点計算に変更する方法をのべる。
GitHub - hiroshi-manabe/JSDecimal: A JavaScript implementation of decimal type.Snap!のサイトでJSDecimalを利用するには,lib/decimal.jsの内容をjavascriptのブロックに入れて実行すれば良い。
最初から固定小数点を利用可能なSnap!を使うには以下の様にローカルにSnap!のソースをダウンロードする。
<script type="text/javascript" src="decimal.js"></script>
次に四則演算のブロックを固定小数点で計算するブロックに変更する。四則演算の計算は,Snap!では,threads.jsに次のように定義されている。
// Process math primtives Process.prototype.reportSum = function (a, b) { return +a + (+b); }; Process.prototype.reportDifference = function (a, b) { return +a - +b; }; Process.prototype.reportProduct = function (a, b) { return +a * +b; }; Process.prototype.reportQuotient = function (a, b) { return +a / +b; };
Process.prototype.reportSum = function (a, b) { return Decimal(+a).add(+b) ; }; Process.prototype.reportDifference = function (a, b) { return Decimal(+a).sub(+b); }; Process.prototype.reportProduct = function (a, b) { return Decimal(+a).mul(+b); }; Process.prototype.reportQuotient = function (a, b) { return Decimal(+a).div(+b); };
これを利用して0.1+0.2=0.3となるかチェックすると次のように「はい」を返し,和を固定小数点で計算したことがわかる。
同様に,「・・・を・・・ずつ変える」ブロックは,同じthreads.jsで
VariableFrame.prototype.changeVar = function (name, delta, sender) { // change the specified variable if it exists // else throw an error, because variables need to be // declared explicitly (e.g. through a "script variables" block, // before they can be accessed. // if the found frame is inherited by the sender sprite // shadow it (create an explicit one for the sender) // before changing the value ("create-on-write") var frame = this.find(name), value, newValue; if (frame) { value = parseFloat(frame.vars[name].value); newValue = isNaN(value) ? delta : value + parseFloat(delta); if (sender instanceof SpriteMorph && (frame.owner instanceof SpriteMorph) && (sender !== frame.owner)) { sender.shadowVar(name, newValue); } else { frame.vars[name].value = newValue; } } };と定義されているので,これを
VariableFrame.prototype.changeVar = function (name, delta, sender) { var frame = this.find(name), value, newValue; if (frame) { value = parseFloat(frame.vars[name].value); newValue = isNaN(value) ? delta : Decimal(value).add(parseFloat(delta)); if (sender instanceof SpriteMorph && (frame.owner instanceof SpriteMorph) && (sender !== frame.owner)) { sender.shadowVar(name, newValue); } else { frame.vars[name].value = newValue; } } };
実行してみると,確かに固定小数点で計算していることがわかる。
もともとの浮動小数点計算の内容をもつブロックを作成しておくと,固定小数点と浮動小数点の計算を随時切り替えることが可能になる。