在shell脚本中添加极大的数字


8

假设两个数字存储在两个不同的文件中,a.txt并且b.txt

每个数字都足够大(超过30个数字),而所使用的数字数据类型不支持该数字bash

如何在外壳中添加它们?


python在这种情况下,我个人将使用或类似的方法。
phk

确定不希望使用sed进行加法运算吗?
杰夫·谢勒

前一段时间,在我的java类中,我们使用堆栈来添加超出java的最大int范围的数字。假设您愿意在bash中使用数组来实现堆栈的麻烦,则可以这样做。。。但这是多余的。。。从下面的答案中可以看出,这是不必要的。或者只是python按照phk的建议使用
Sergiy Kolodyazhnyy

Answers:


12

假设它们是十进制数字,则可以执行以下操作:

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" )"

如果<<<语法感觉很奇怪(它被称为“ here-string”,并且是POSIX shell语法bash以及其他一些shell所支持的扩展),则可以改用printf将附加内容发送给bc

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

c再次存储结果:

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

1 使用bash执行增加了两个非常大的数字将需要执行,在bash脚本,做例行的高精度计算。这是完全可行的,但麻烦且不必要的,因为每个Unix都bc已经以相对容易和可访问的方式为您提供了此服务。


1
或者,您可以这样做read a < a.txt。如果有的话(假设$IFS尚未修改),这也可以消除开头和结尾的空白。
斯特凡Chazelas

1
为什么在流程替换内的here-string的引号内的引号不需要转义?
布莱斯·昆塔

2
@BryceGuinta因为与之类似echo "\"hello\"",@中的东西$(...)不是作为参数传递给另一个程序的字符串,并且Shell知道如何处理引号的嵌套。这也是为什么使用$(...)反引号比使用反引号更好的原因。您可以$( ... $( ... ) )毫无歧义地进行写作,而使用反引号的相同之处是……笨拙。
库萨兰达

但是如何在不使用bc的bash中进行操作
voldemort619

@ voldemort619您必须像这些库中的任何一个一样以类似的方式实现加法。您可以查看此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比较留在了那里,但是为了进行比较而将其注释掉了。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.