Shellで数値計算もどき【dnalop-クソデカ電卓編1】桁あふれに打ち勝つ足し算

*注意* この記事はQiitaに上げられるほどの信頼性を有していないことからエピソードトークとしてnoteに上がっています

イントロ

桁あふれ、カンスト、などなどコンピュータで数字を扱うときには厄介な問題がある。これは数値を2進数で表したとき使用できる桁数が限られているためだ。
それなら、扱う値を数値ではなく文字列とみなせばよいではないか。文字列ということで今回も今回とてシェルスクリプトを振り回してみよう。
Githubに最新のコードを置いた。大きな変化がなければdnalop.shのaddという関数に処理が入っている。
https://github.com/ogawa3427/falsestatistics/commit/ad9948afd3cc18f4b98c830f8a41c9b647ac6700

概観

"大人に変わったら逆ポーランド記法電卓に逢いたくなる"
谷村新司もそう言っているから逆ポーランド記法を目指す。ただし今回は正の整数&0の加算でかつ演算子は1つまで、純粋に加算だけを今回は書く。

第1、第2引数に来た文字列をそれぞれ逆順に1文字ずつ読みだす。2数と前ループから来た繰り上がりフラグを加算して結果の1の位を一次ファイルに書きこむ。結果が10以上なら繰り上がりフラグを立てる。ここまでを繰り返してファイルが終わったら繰り上がりフラグを書き込んで、逆順に出力しておわり。

ファイルに入っている文字列を逆順にする

function gyaku () {
  echo -n "" > stemp
  exec 3<"$1"
  while true; do
    IFS= read -r -n1 -u3 char
    if [ -z "$char" ]; then
      break
    fi
    echo -n "$char" > onetemp
    cat onetemp stemp > temptemp
    rm stemp
    rm onetemp
    mv temptemp stemp
  done
  exec 3<&-
  rm "$1"
  mv stemp "$1"
}

1文字づつファイルから吐き出して記録用の一時ファイルの先頭に書き込む。これをループする、ChatGPTですぐ丸ごと書けそう。

桁数が揃っていない場合に対応する

gyaku farg
gyaku sarg

exec 3<"farg"
exec 4<"sarg"

flag=0
echo -n > rawout

while true; do
	IFS= read -r -n1 -u3 char1
	IFS= read -r -n1 -u4 char2

ループ開始前に繰り上がりフラグを0で定義しておく
入力を1文字づつ逆順で吐き出す

	if [ -z "$char1" ] && [ -z "$char2" ]; then
		break
	fi

第1、第2引数の内容が両方終了したらループを出る

	[ -z "$char1" ] && char1="0"
	[ -z "$char2" ] && char2="0"

片方のファイルが続いている場合、終了しているほうの出力は0にしておく

位ごとに加算して書き込み次ループに送る

	#ワイの答えはこれや!
	sum=$((char1 + char2))
	sum=$((sum + flag))

	#記録&go next
	if [ $sum -lt 10 ]; then
		echo -n "$sum" >> rawout
		flag=0
	elif [ $sum -eq 10 ]; then
		echo -n "0" >> rawout
		flag=1
	else
		sum=$((sum - 10))
		echo -n $sum >> rawout
		flag=1
	fi
done
exec 3<&-
exec 4<&-

第1引数から来た数、第二引数から来た数、繰り上がりフラグをぞれぞれ足して各位での計算結果を得る。計算結果を記録用ファイルに書き込んで、10より大きければ繰り上がりフラグを立てて次ループにループに送る。

仕上げ

echo $flag | sed 's/0//g' >> rawout
gyaku rawout

cat rawout
echo

長いほうの入力の最後の文字を処理した次のループの頭で抜けて、送られていた繰り上がりフラグを書き込んだり書き込まなかったりする。

完成した計算結果は文字が逆順で書き込まれているため、最初と同様にひっくり返して出力して終了する。

懸念

今回のお手製計算機の目標は実用範囲などを度外視してとにかく桁あふれに打ち勝つということであった。一桁どうしを除いた単純な加算はもちろん、文字数を数えるなど、巨大な入力を扱った場合にBashで扱えない数値が発生する可能性のある操作をできる限り排除した。これにより、ファイルサイズやメモリ容量などの制限に引っかからない限りどれだけ大きな入力でも正確に計算できるはずである。
しかし、私の知らないところで入力/出力が数値として扱われている可能性や文字列を数えるような処理が走っている可能性があり、未知の不具合が発生する可能性に怯えている。
今後は電子計算機の基礎的な部分とUNIXコマンドのソースおよびC言語の使用をチラ見しつつゆっくり小数・負の数やほかの四則演算に対応してゆく予定。
ここまでお読みいただきありがとうございました。


この記事が気に入ったらサポートをしてみませんか?