なぜ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-318awkでは、これらの小さな数字()を処理する際にいくつかの問題があります。

  • まず、入力を使用する前に数値に変換する必要があります。これは通常ゼロを加えることによって行われます。したがって、次のようなものが必要です。

    val=0+$1
    
  • 2. 一般倍精度浮動小数点数(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個を超える数字は切り捨てられます。もっと重要な番号を確認するように依頼してください。 15と同じです%.15g(53桁の歌手に17以上を要求しないでください。嘘をつくこともできます)。

  • 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).

おすすめ記事