シェルスクリプトでの非常に大きな数の追加


8

二つの数が2つの異なるファイルに格納されていると仮定し、a.txtb.txt

各数値は、が使用する数値データタイプでサポートされないほど十分に大きい(30桁を超える)bash

それらをシェルに追加するにはどうすればよいですか?


個人的に私はpythonその場合に使用します。
2017年

代わりにsedを追加使用したくないのですか?
ジェフシャラー

少し前、私のJavaクラスでは、Javaの最大整数範囲外の数値を追加するためにスタックを使用しました。bashで配列を使用してスタックを実装する問題に進んで進んでいると想定すると、それを行うことができます。。。しかし、それは非常に冗長です。。。以下の回答からわかるように、不要です。またはpython、phkの提案どおりに使用する
Sergiy Kolodyazhnyy 2017年

回答:


12

それらが10進数であると仮定すると、次のことができます。

paste -d + a.txt b.txt | bc

bc非常に長い数値(実装によっては68桁または69桁以上)を改行することに注意してください。GNU bcでは、次のBC_LINE_LENGTHように環境変数を0に設定することで無効にできます。

paste -d + a.txt b.txt | BC_LINE_LENGTH=0 bc

10

トリックは、加算を実行するために使用bashないことです1

まず、各数値を個別の変数に読み取ります。これは、ファイルに含まれているのは数字のみで、他の情報は含まれていないことを前提としています。

a="$(<a.txt)"
b="$(<b.txt)"

次に、bc計算機を使用して結果を取得します。

bc <<<"$a + $b"

bc 「任意精度の算術言語と計算機」です。

結果を変数に格納するにはc

c="$( bc <<<"$a + $b" )"

<<<構文が奇妙に感じられる場合(これは「ヒア文字列」と呼ばれ、bashと他のいくつかのシェルでサポートされているPOSIXシェル構文の拡張機能です)、代わりにを使用printfして、に追加を送信できますbc

printf '%s + %s\n' "$a" "$b" | bc

そして結果をc再度保存します:

c="$( printf '%s + %s\n' "$a" "$b" | bc )"

1 を使用bashして2つの非常に大きな数の加算を実行するには、bashスクリプトで、任意精度の演算を実行するルーチンの実装が必要になります。これは完全に実行可能ですが、すべてのUnixにbcはこのサービスが比較的簡単でアクセス可能な方法ですでに提供されているため、面倒で不要です。


1
または、あなたがすることができますread a < a.txt。これにより、先頭と末尾の空白が削除$IFSされても処理されます(変更されていないと想定)。
ステファンChazelas

1
引用符内の引用符をプロセス置換内のhere-stringでエスケープする必要がないのはなぜですか?
ブライスギンタ2017年

2
@BryceGuintaのようなものとは異なりecho "\"hello\""、内のもの$(...)は別のプログラムに引数として渡される文字列ではなく、シェルは引用符のネストの処理方法を知っています。これは$(...)、バッククォートではなくバッククォートを使用する方が良い理由でもあります。$( ... $( ... ) )曖昧さなしに書くことができますが、バッククォートを使用する同じことは...厄介です。
クサラナンダ

しかし、bcを使用せずにbashで行う方法
voldemort619

@ voldemort619 これらのライブラリのいずれかと同じようにadditiionを実装する必要があります。説明については、このStackOverflowの回答ご覧ください。ただし、実際にはを使用してくださいbc
Kusalananda

3

StéphaneKusalanandaの両方が言ったように、「本当に、単にbcを使用してください」、しかし本当にbashを追加に使用したい場合は、ここから出発点(正の整数のみ)-読者が実装する練習として残しておきます小数と負の数:

function arbadd {
  addend1=$1
  addend2=$2
  sum=
  bcsum=$(echo $addend1 + $addend2 | BC_LINE_LENGTH=0 bc)

  # zero-pad the smallest number
  while [ ${#addend1} -lt ${#addend2} ]
  do
    addend1=0${addend1}
  done

  while [ ${#addend2} -lt ${#addend1} ]
  do
    addend2=0${addend2}
  done

  carry=0
  for((index=${#addend1}-1;index >= 0; index--))
  do
    case ${carry}${addend1:index:1}${addend2:index:1} in
      (000) carry=0; sum=0${sum};;
      (001|010|100) carry=0; sum=1${sum};;
      (002|011|020|101|110) carry=0; sum=2${sum};;
      (003|012|021|030|102|111|120) carry=0; sum=3${sum};;
      (004|013|022|031|040|103|112|121|130) carry=0; sum=4${sum};;
      (005|014|023|032|041|050|104|113|122|131|140) carry=0; sum=5${sum};;
      (006|015|024|033|042|051|060|105|114|123|132|141|150) carry=0; sum=6${sum};;
      (007|016|025|034|043|052|061|070|106|115|124|133|142|151|160) carry=0; sum=7${sum};;
      (008|017|026|035|044|053|062|071|080|107|116|125|134|143|152|161|170) carry=0; sum=8${sum};;
      (009|018|027|036|045|054|063|072|081|090|108|117|126|135|144|153|162|171|180) carry=0; sum=9${sum};;
      (019|028|037|046|055|064|073|082|091|109|118|127|136|145|154|163|172|181|190) carry=1; sum=0${sum};;
      (029|038|047|056|065|074|083|092|119|128|137|146|155|164|173|182|191) carry=1; sum=1${sum};;
      (039|048|057|066|075|084|093|129|138|147|156|165|174|183|192) carry=1; sum=2${sum};;
      (049|058|067|076|085|094|139|148|157|166|175|184|193) carry=1; sum=3${sum};;
      (059|068|077|086|095|149|158|167|176|185|194) carry=1; sum=4${sum};;
      (069|078|087|096|159|168|177|186|195) carry=1; sum=5${sum};;
      (079|088|097|169|178|187|196) carry=1; sum=6${sum};;
      (089|098|179|188|197) carry=1; sum=7${sum};;
      (099|189|198) carry=1; sum=8${sum};;
      (199) carry=1; sum=9${sum};;
    esac
  done
  if [ $carry -eq 1 ]
  then
    sum=1${sum}
  fi
  printf "Sum = %s\n" "$sum"
}

bcそこに比較を残しましたが、比較のためにコメント化しました。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.