awkの浮動小数点比較が期待した結果を生成しないのはなぜですか?

awkの浮動小数点比較が期待した結果を生成しないのはなぜですか?

次のawkスクリプトがあります。

{
    if ($1 > 1000) {
        print $0
    }
}

最初で唯一の列の値が1000を超えるすべての行を印刷する必要があります。

テストデータは次のとおりです。

1,151
1001,055
756,75788

を使用すると、awk -f my_script.awk my_data次の結果が表示されます。

1001,055
756,75788

私が期待するもの:

1001,055

awkのバージョンは次のとおりです。

GNU Awk 5.0.0, API: 2.0 (GNU MPFR 4.0.2, GNU MP 6.1.2)

私は何が間違っていましたか?

編集する:

コメントで述べたように:

ここで、カンマは区切り文字ではなく、フランス語で使用される小数点区切り文字で、Wikipediaによると、英語以外のすべての表記システムで使用されています。

編集2:サンプルデータには1列しかありません。実際のデータでは、フィールド区切り文字は「;」です。

答え1

婦人声明以下の最初の回避策は、元の問題に対する誤解のために使用されなくなりました。一致する解決策については、編集1と2を参照してください。


awkカンマはデフォルトでは区切り文字として認識されません。タブとスペースに対してのみこれを行います。したがって、区切り文字を明示的に定義する必要があり、そうでない場合はawk文字列値を比較する必要があります。

BEGIN {FS=","}
$1 > 1000

条件が満たされると、1行を印刷する簡単な表記法も使用しています。これは単純なコードのヒントです。

または、コマンドラインで区切り文字を指定します。

awk -F,  -f script.awk infile

編集1以下の仕様は、,小数点区切り文字として使用されます。小数点区切り文字awkとして扱われ、小数点区切り文字を使用するロケールは問題になることがよくあります。.

オプション1の場合は、少しトリックをお勧めします。整数と分数をコンマ区切りの別々のフィールドとして維持し、個別に評価します。

 BEGIN {FS=","}
 $1==1000 && $2>0 || $1 > 1000

その後、a)ロケールの使用をスキップし、b)awk-と-区切りの間を前後に翻訳する試みをスキップします。欠点は、浮動小数点データが多い場合、フィールド番号が列ヘッダーと一致しない可能性があることです。ただし、実際に一致する行だけを印刷する場合は機能しません。,.

このように入力

1,151
1001,055
756,75788
1000
1000,00
1000,000001

戻ってくる

1001,055
1000,000001

編集2別の、おそらくよりエレガントなオプションは、比較のために最初のフィールドをドットで区切られた浮動小数点に変換することです。

gensub(/,/,".","g",$1)+0 > 1000

これは次のように機能します。フィールド 1 を文字列として解釈し、 で置換し,.追加して0-logic で数値にしawk、条件が true の場合比較して印刷します。利点は、;フィールド区切り記号を使用してこのソリューションでフィールド番号付けの問題が発生しないことです。


一般に、,可能であれば、小数点区切り文字を使用しないことをお勧めします。もちろん、これはデータを提供する人によって異なります。

答え2

@Ed Mortonと@steeldriverのコメントを回答に追加するには、GNU awkにコンマを小数点区切り文字として扱い、有効にするか、--posix/--use-lc-numeric定義されたロケールを使用させることができます-N

たとえば、

$ LC_NUMERIC=fi_FI.UTF-8 awk -N '$1 > 1000' data.txt 
1001,055

または:

$ LC_NUMERIC=fi_FI.UTF-8 awk --posix '$1 > 1000' data.txt 
1001,055

点を小数点区切り文字として扱う限り、そのようなものは数字756,75788として認識されず、文字列として認識され、比較は文字列ベースです。7後の並べ替え1,前の並べ替え0、したがって756,75788>10001,151<です1000。 (ロケールの照合規則も使用しているかどうかはわかりませんが、,解釈方法に影響を与える可能性があります。)

($1 + 0)以下を使用して、値を数値として扱うように強制できます。これは質問のデータで機能するようですが、たとえば印刷1000,1され1000ません。 「1000より大きい」ではなく「最小1000」を確認するには、小数($1 + 0) >= 1000部を使用して無視してください。

望むより:6.1.4.2 ロケール環境が変換に影響を与えるそして6.3.2.1 文字列型と数値型GNU awk マニュアルにあります。(後のページの例は、37<42比較がテキストなのか数値なのかは重要ではないため、愚かなものです。)

関連情報