bcコマンドの限界は68桁?(回避方法あり

More than 3 years have passed since last update.

hello worldより「コラッツの問題」書くほうがいろいろ学べそうという投稿でシェルスクリプトでコラッツの問題を書きました。


collatz.sh

#!/bin/sh

collatz() {
n=$1
echo $n
sleep 0.5s
if [ `echo "$n % 2" | bc` -eq 1 ] && [ $n != "1" ]; then
collatz `echo "3 * $n + 1" | bc`
elif [ `echo $n % 2 | bc` -eq 0 ]; then
collatz `echo $n / 2 | bc`
fi
}

collatz $1


この投稿で


99999999999999999999999999999999999999999999999999999999999999995まで計算出来ました。


と書いたのですが、

99999999999999999999999999999999999999999999999999999999999999996はなぜダメだったかというとエラーが出ていたのです。

※環境はmacです。

99999999999999999999999999999999999999999999999999999999999999996

49999999999999999999999999999999999999999999999999999999999999998
・・・(省略
36947297000885009765624999999999999999999999999999999999999999999999
11084189100265502929687499999999999999999999999999999999999999999999\
(standard_in) 1: illegal character: \
collatz.sh: line 6: [: -eq: unary operator expected
(standard_in) 1: illegal character: \
collatz.sh: line 8: [: -eq: unary operator expected

68桁でバックスラッシュが入ってしまい数値化できずエラーというところでしょうか。

「なんで68桁でバックスラッシュが入るのかなぁ」と思ってbcに大きい値を入れてみると

$ echo 9999999999999999999999999999999999999999999999999999999999999999999999999999999 | bc

99999999999999999999999999999999999999999999999999999999999999999999\
99999999999

バックスラッシュが入りました。

どうやらbcが68桁でバックスラッシュを入れているみたいです。


bcのmanを見る

man見たら何かわかるだろと思い、「bc man」でググって一番上のMan page of bc - JM Project

環境変数の項でなんとなく関係ありそうな説明を発見


BC_LINE_LENGTH

数字を出力するときの 1 行の文字数を整数で指定します。 数字が長過ぎると、バックスラッシュと改行を含めた出力となります。


これっぽい

現在のBC_LINE_LENGTHを見ようと

$ echo $BC_LINE_LENGTH


としてみても空行が返されるだけでした。

デフォルトで68桁なのでしょうか?

「BC_LINE_LENGTH」でググると

$ BC_LINE_LENGTH=999 echo 9999999999999999999999999999999999999999999999999999999999999999999999999999999 | bc

$ export BC_LINE_LENGTH=999

で変えられると書いてありました。

環境変数はなるべく汚したくなかったので最初は前者でやったのですが効きませんでした。

$ BC_LINE_LENGTH=999 echo 9999999999999999999999999999999999999999999999999999999999999999999999999999999 | bc

99999999999999999999999999999999999999999999999999999999999999999999\
99999999999

バックスラッシュで改行されます。

なので今度は後者で。

$ export BC_LINE_LENGTH=999

$ echo 9999999999999999999999999999999999999999999999999999999999999999999999999999999 | bc
9999999999999999999999999999999999999999999999999999999999999999999999999999999

改行されません!思ったとおりに動くと楽しいですね!

ではこれで最初の「コラッツの問題」をやってみましょう!!

$ export BC_LINE_LENGTH=999

$ sh collatz.sh 99999999999999999999999999999999999999999999999999999999999999996
・・・(省略
24921477006304688
12460738503152344
collatz.sh: line 6: [: -eq: unary operator expected
Segmentation fault: 11

エラーで落ちました。

「Segmentation fault: 11」はセグメンテーション違反というアクセスしてはいけないメモリにアクセスした時に起きるエラーのようです。

再帰処理だし、桁数多いからメモリ使うだろうしで、これはどうしようもないかもしれないですね(^_^;)

(再帰じゃなくてwhileで回す処理ならメモリ食わないのかな?とか考え中。)