为什么 gawk(有时?)认为 2.0e-318 > 2.0?

为什么 gawk(有时?)认为 2.0e-318 > 2.0?

我正在尝试使用 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 位的数字将被截断。要获得更重要的数字:询问它们。比如%.15g15(对于 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

相关内容