Simple Scheme で電卓をつくってみる(12) ― 2倍、0、2乗、1 ―

加減が揃い、また、エラー処理ができるようにもなりました。

加減乗除を使った機能を追加しましょう。
電卓で×=と打ち込むと、それまでの計算結果にそれまでの計算結果をかけた値―つまり「それまでの計算結果」の2乗が得られます。
÷=だとそれまでの計算結果をそれまでの計算結果で割った値が得られます。
追加するのはこの機能です。

ちなみに+=や-=は、参考にしている電卓(SL-720L)を動かしてみてもそれまでの計算結果にそれまでの計算結果を足したり引いたりしませんが*1、演算の種類で区別すると条件式が長くなるので加減乗除すべてにこの機能をつけることにします*2

(define input=
  (let ((op (make-op 0)))
    (lambda ()
      (if (symbol=? mode waiting)
          (begin (set! mode inputting) (set! input result) (op) (set! mode waiting))
          (op)))))

(↓2019.11.28 追記)

例えば 3 + 4 × = と入力したときのinputresult(とwindow)の値をトレースしてみましょう。各ボタンが入力されたあとのそれぞれの変数の値を表にします。

初期値3+4×=
input0347
result03749
window0inputresultinputresultresult
(list-ref ops op-no)get2ndaddmulget2nd

3 + 4 × まで押されたとき、モードはwaitingです。また、次に行う計算として、mulが指定されています。
ここでもし演算ボタンを押したらmulに代わってその演算が次に行う計算として指定されます。waitingモードなのでそれ以外に何もしません。計算が実行されるのはinputtingモードのときです。
inputtingモードのときに演算ボタンが押されると、直前に押された演算がresultinputに適用されます(そのあとで押された演算ボタンに基づいて次に行う計算が指定されます)。

input=はこの演算ボタンの機能を使い回しています。
ここではresultうしの計算をしたいので、モードを一時的にinputtingに変更し、resultinputに代入してから演算ボタンの関数*3を呼び出し、その後モードをもとに戻しています。

(↑追記ここまで)

この機能を使って263を概算してみました。64ビット符号つき整数の最大値ですね*4

922.33715という結果が得られましたが桁溢れエラーを1度解除してさらに×=したので16桁右にずれています。だから実際の結果は922.33715×1016です。

また、誤差が生じているのでここから先は手計算で。

まず2のあとに×=を5回繰り返して入力して得られる表示によって、

$$ 42.949672 ≦ 2^{32}\times10^{-8} < 42.949672+10^{-6} $$


であることがわかります。各辺を2乗すると、

$$ 42.949672^2 ≦ 2^{64}\times10^{-16} < 42.949672^2+2\cdot42.949672\times10^{-6}+10^{-12} $$


となります。右辺が複雑なので$2\cdot42.949672\times10^{-6}+10^{-12}<86\times10^{-6}$であることを利用して書き替えます。

$$ 42.949672^2 ≦ 2^{64}\times10^{-16} < 42.949672^2+86\times10^{-6} $$


(エラー解除後)6回目の×=の入力後得られる表示によって、

$$ 1844.6743≦42.949672^2<1844.6743+10^{-4} $$


であることがわかります。これとひとつ前の式から次の関係が得られます。

$$ 1844.6743 ≦ 2^{64}\times10^{-16} < 1844.6743+10^{-4}+86\times10^{-6} $$


最後に1844.6743を2で割る際には丸め誤差は生じていないので他の部分もそのまま2で割って、

$$ 922.33715≦2^{63}\times10^{-16}<922.33715+\left(10^{-4}+86\times10^{-6}\right)/2 $$


が得られます。これを整理して

$$ 9.2233715\times10^{18}≦2^{63}<\left(9.2233715+9.3\times10^{-7}\right)\times10^{18} $$


という関係が得られます。上位6桁は8桁電卓で正確にわかるということですね*5

×=以外のテストもやったけど録画してない( ´△`)

続きます。

(ここまでのソースコード)

*1:前者は×2=で代用できますし後者の結果は0なので、この機能がないのは不要だからかもしれません。しかしそれだと結果が常に1になる÷=にこの機能があるのは変なので、単に手元のが壊れているだけかもしれません。あるいは、この電卓にM+ボタンやM-ボタンがあることとなにか関係があるのかもしれません。

*2:なにか問題がありそうなら変えます。メモリ関連でややこしいことになりそうな気が少しする。

*3:演算ボタンを生成する関数によって生成される関数。実行後は次に行う計算として、get2ndが指定されます。

*4:最大値は本当は263-1ですが。

*5:もっと正確かつもっと簡単に調べる方法は電卓以外でいくらでもありますけど。