我正在尝试使用 gawk 查找一列数据的最大值:
gawk 'BEGIN{max=0} {if($1>0+max) max=$1} END {print max}' dataset.dat
看起来dataset.dat
像这样:
2.0
2.0e-318
命令的输出是
2.0e-318
显然小于 2。
我的错误在哪里?
编辑
有趣的是,如果交换输入文件的行,输出就会变成
2.0
编辑2
我的 gawk 版本是 GNU Awk 4.2.1,API:2.0(GNU MPFR 4.0.2,GNU MP 6.1.2)。
答案1
2e-318
在 awk 中处理如此小的数字 ( ) 存在几个问题。
首先,输入需要在使用之前转换为数字。这通常是通过添加 0 来完成的。因此,您需要类似的内容:
val=0+$1
二、普通双精度浮点数(53位尾数和11位指数)指数的 11 位宽度允许表示 10e-308 和 10e308 之间的数字,因此,普通浮点数将无法表示这样的数字。
$ echo '1e-307 1e-308' | awk '{print $1,$1+0,$2,$2+0}' 1e-307 1e-307 1e-308 0
默认 GNU awk 将不接受以下(正常)值
1e-308
。第三,(CNVFMT 和 OFMT)的默认转换格式
awk
设置为"%.6g"
.有效数字超过 6 位的数字将被截断。要获得更重要的数字:询问它们。比如%.15g
15(对于 53 位尾数不要要求超过 17,它可能会撒谎)。第四,最好将 的第一个值设置
max
为第一个输入。如果输入的最大值为负,则将 max 设置为 0 将失败。
如果您使用 GNU awk 并且它已以任意精度编译,您可以使用:
$ printf '%s\n' 2e-318 2e-317 2e-307 2e-308 2e-319 |
awk -M -v PREC=100 'BEGIN{OFMT="%.15g"};
{val=0+$1};
NR==1{max=val};
{print($1,val,max)};
val>max{max=val}
END{print max}'
2e-318 2e-318 2e-318
2e-317 2e-317 2e-318
2e-307 2e-307 2e-317
2e-308 2e-308 2e-307
2e-319 2e-319 2e-307
2e-307
或者简化为您的用例:
awk -M -v PREC=100 '
BEGIN{OFMT="%.15g"}; # allow more than 6 figures
{val=0+$1}; # convert input to a (float) number.
NR==1{max=val}; # On the first line, set the max value.
val>max{max=val} # On every entry keep track of the max.
END{print max} # At the end, print the max.
' file # file with input (one per line).
答案2
0+ 需要作为每个 $1 的前缀来强制进行数字转换。 max 不需要 0+ ——它在存储时已经转换为数字。
Paul--) AWK='
> BEGIN { max = 0; }
> 0+$1 > max { max = 0 + $1; }
> END { print max; }
> '
Paul--) awk "${AWK}" <<[][]
> 2.0
> 2.0e-318
> [][]
2
Paul--) awk "${AWK}" <<[][]
> 2.0e-318
> 2.0
> [][]
2