awk浮動小数点演算を使用した驚くべき結果

awk浮動小数点演算を使用した驚くべき結果

私は、ある行から次の行にいくつかの値を渡す単純な算術演算を実行するためにawkを取得しようとしました。

以下は比較のための最小例のペアです。最初の例は、99.16 - 20.85 = 78.31以降の予想される動作です。

$ echo -e "0,99.16\n20.85,78.31" | awk -F, '{
  if (NR != 1 && (prior_tot - $1) != $2) {
    print "Arithmetic fail..." $0
  } else {
    print "OK"
  };
  prior_tot = $2
}'

商品を返す

OK
OK

2番目の例は99.15 - 20.85 = 78.30なので、予想される動作ではありません。

$ echo -e "0,99.15\n20.85,78.30" | awk -F, '{
  if (NR != 1 && (prior_tot - $1) != $2) {
    print "Arithmetic fail..." $0
  } else {
    print "OK"
  };
  prior_tot = $2
}'

商品を返す

OK
Arithmetic fail...20.85,78.30

何が起こっているのかを説明できる人はいますか?

ベストアンサー1

浮動小数点数99.15、28.85、および78.30には、正確なIEEE 754バイナリ表現はありません。同じ計算を実行するCプログラムを使用してこれを確認できます。

#include <stdio.h>
int
main(int ac, char **av)
{
        float a = 99.15;
        float b = 20.85;
        float c;

        printf("a = %.7f\n", a);
        printf("b = %.7f\n", b);
        c = a - b;
        printf("c = %.7f\n", c);

        return 0;
}

x86とx86_64システムでこれらの答えを得ました。おそらく両方がこれを行うからです。IEEE 754浮動小数点数学:

a = 99.1500015 b = 20.8500004 c = 78.3000031

何が起こるかは次のとおりです。浮動小数点数は、符号ビット(正または負)、複数ビット、および指数で表されます。すべての有理数(つまり、この文書の「浮動小数点数」)がIEEE 754形式で正確に表現できるわけではありません。したがって、ハードウェアはできるだけ近いです。残念ながら、あなたのテストケースでは、ハードウェアはこれら3つの値を正確に表現できません。double代わりに使用してもそうではありません。おそらくそうfloatですawk

これは追加の説明正確なバイナリ表現を使用した浮動小数点数の間隔。

一部の値はテストに合格しますが、他の値は失敗する可能性があります。そうでない場合が多いです。

一般に、人々は以下を実行して浮動小数点問題を解決します。

if (abs(c) <= epsilon) {
    // We'll call it equal
} else {
    // Not equal
}

でこれを行うのははるかに難しいですawk。通貨単位と2つの有効数字を持つサブユニット(ドルやセントなど)を使用して通貨計算を実行する場合は、すべての計算をサブユニット(米国の場合はセント)で実行する必要があります。通貨計算に浮動小数点を使用しないでください。あなたはその決定を後悔するでしょう。

おすすめ記事