2. 浮点运算

letexpr 都无法进行浮点运算,但是 bcawk 可以。

范例:求 1 除以 13,保留 3 位有效数字

$ echo "scale=3; 1/13"  | bc
.076

$ echo "1 13" | awk '{printf("%0.3f\n",$1/$2)}'
0.077

说明: bc 在进行浮点运算时需指定精度,否则默认为 0,即进行浮点运算时,默认结果只保留整数。而 awk 在控制小数位数时非常灵活,仅仅通过 printf 的格式控制就可以实现。

补充:在用 bc 进行运算时,如果不用 scale 指定精度,而在 bc 后加上 -l 选项,也可以进行浮点运算,只不过这时的默认精度是 20 位。例如:

$ echo 1/13100 | bc -l
.00007633587786259541

范例:余弦值转角度

bc -l 计算,可以获得高精度:

$ export cos=0.996293; echo "scale=100; a(sqrt(1-$cos^2)/$cos)*180/(a(1)*4)" | bc -l
4.934954755411383632719834036931840605159706398655243875372764917732
5495504159766011527078286004072131

当然也可以用 awk 来计算:

$ echo 0.996293 | awk '{ printf("%s\n", atan2(sqrt(1-$1^2),$1)*180/3.1415926535);}'
4.93495

范例:有一组数据,求人均月收入最高家庭

在这里随机产生了一组测试数据,文件名为 income.txt

1 3 4490
2 5 3896
3 4 3112
4 4 4716
5 4 4578
6 6 5399
7 3 5089
8 6 3029
9 4 6195
10 5 5145

说明:上面的三列数据分别是家庭编号、家庭人数、家庭月总收入。

分析:为了求月均收入最高家庭,需要对后面两列数进行除法运算,即求出每个家庭的月均收入,然后按照月均收入排序,找出收入最高家庭。

实现:

#!/bin/bash
# gettopfamily.sh

[ $# -lt 1 ] && echo "please input the income file" && exit -1
[ ! -f $1 ] && echo "$1 is not a file" && exit -1

income=$1
awk '{
    printf("%d %0.2f\n", $1, $3/$2);
}' $income | sort -k 2 -n -r

说明:

  • [ $# -lt 1 ]:要求至少输入一个参数,$# 是 Shell 中传入参数的个数
  • [ ! -f $1 ]:要求输入参数是一个文件,-f 的用法见 test 命令,help test
  • income=$1:把输入参数赋给 income 变量,再作为 awk 的参数,即需处理的文件
  • awk:用文件第三列除以第二列,求出月均收入,考虑到精确性,保留了两位精度
  • sort -k 2 -n -r:这里对结果的 awk 结果的第二列 -k 2,即月均收入进行排序,按照数字排序 -n,并按照递减的顺序排序 -r

演示:

$ ./gettopfamily.sh income.txt
7 1696.33
9 1548.75
1 1496.67
4 1179.00
5 1144.50
10 1029.00
6 899.83
2 779.20
3 778.00
8 504.83

补充:之前的 income.txt 数据是随机产生的。在做一些实验时,往往需要随机产生一些数据,在下一小节,我们将详细介绍它。这里是产生 income.txt 数据的脚本:

#!/bin/bash
# genrandomdata.sh

for i in $(seq 1 10)
do
    echo $i $(($RANDOM/8192+3)) $((RANDOM/10+3000))
done

说明:上述脚本中还用到seq命令产生从1到10的一列数,这个命令的详细用法在该篇最后一节也会进一步介绍。