我有以下 test.txt 文件 -
var,value
a,1.1234
b,1.7896749
c,2.4982
d,1.2976232
当我使用以下命令时 -
awk -F ',' '{SUM+=$2}END{print SUM}' test.txt
它打印6.7089
但保留所有小数位的结果是6.7088981 如何编写命令,以便打印结果中的所有小数位,不仅在这种特定情况下,而且在一般情况下。例如,如果结果有10位小数,那么它应该打印所有10位小数吗?如果结果只有 5 位小数,则应该只打印 5 位小数。我使用的操作系统是 Red Hat Enterprise Linux Server 7.7
答案1
OFMT
打印时,使用包含printf
格式规范的特殊变量(默认情况下)将非整数转换为十进制字符串表示形式%.6g
。您可以将其更改为%.17g
以获得 IEEE 754 双精度二进制浮点数的最大精度(awk
大多数系统上的大多数实现在内部使用)。另一个变量 ( CONVFMT
) 用于浮点数隐式转换为字符串的其他情况(例如将数字与其他内容连接时)
使用这些双精度数不会获得更高的精度,超过 17 没有任何意义。已经使用 17,您可能会看到一些伪影。如果您不需要那么高的精度,15 位有效数字可能会更好。
$ awk -v OFMT=%.17g -F ',' '{SUM+=$2};END{print SUM}' < file
6.7088981000000008
$ awk -v OFMT=%.15g -F ',' '{SUM+=$2};END{print SUM}' < file
6.7088981
虽然OFMT
会影响所有打印的浮点数,但您也可以printf
直接使用以所需的精度打印数字。
$ awk -F ',' '{SUM+=$2};END{printf "%.15g\n", SUM}' < file
6.7088981
自版本 4.1.0 起, GNU 实现awk
也可以使用任意精度算术支持进行编译(请参阅 参考资料info gawk 'Arbitrary Precision Arithmetic'
)。如果您的系统出现这种情况,您还可以执行以下操作:
gawk -M -v PREC=256 -v OFMT=%.60g -F ',' '{SUM+=$2};END{print SUM}' < file
例子:
$ printf 'x,%s\n' 1 1000000000000000000000000000000000.00000000001 |
> gawk -v OFMT=%.15g -F ',' '{SUM+=$2};END{print SUM}'
999999999999999945575230987042816
$ printf 'x,%s\n' 1 1000000000000000000000000000000000.00000000001 |
> gawk -M -v PREC=256 -v OFMT=%.60g -F ',' '{SUM+=$2};END{print SUM}'
1000000000000000000000000000000001.00000000001
这里的另一种方法可以是使用bc
(假设这些数字总是这样表达(0.001
,不是1e-3
例如)):
<file tail -n+2 | # skip header
cut -d, -f2 | # extract second field
paste -sd + - | # join input lines with +
bc
后面的位数.
将是任何输入记录中的最大位数。
答案2
答案3
正如已经讨论过的,当试图获得直观的答案时,浮点运算是一个问题,但如果您知道您的输入在“.”之前最多只能有 3 位数字。比如说 9 之后,您可以使用字符串操作将数字转换为小数,然后将它们相加以避免浮点算术问题,然后在打印之前将结果再次转换回 FP,例如:
$ cat tst.awk
BEGIN {
FS = ","
bef = 3
aft = 9
}
NR>1 {
split($2,f,".")
val = sprintf("%*s%-*s",bef,f[1],aft,f[2])
gsub(/ /,0,val)
sum += val
}
END {
sub(".{"aft"}$",".&",sum)
sub(/0+$/,"",sum)
print sum
}
$ awk -f tst.awk file
6.7088981
如果 3 和/或 9 对您来说不够大,请选择其他数字或执行 2 遍方法,在第一遍中计算出每个数字的最大值,例如:
$ cat tst.awk
BEGIN { FS = "," }
FNR==1 { next }
{ split($2,f,".") }
NR==FNR {
bef = (length(f[1]) > bef ? length(f[1]) : bef)
aft = (length(f[2]) > aft ? length(f[2]) : aft)
next
}
{
val = sprintf("%*s%-*s",bef,f[1],aft,f[2])
gsub(/ /,0,val)
sum += val
}
END {
sub(".{"aft"}$",".&",sum)
sub(/0+$/,"",sum)
print sum
}
$ awk -f tst.awk file file
6.7088981