
区切りファイルを読み取って、各レコードのファイル内の 3 番目の要素を加算するスクリプトがあります。データ ファイルの大部分では、1 つを除いて正常に動作します。データ ファイルに 193 のレコードがあるデータ ファイルが 1 つあります。スクリプトから 2028219.43 が返されることを期待しています。代わりに、切り上げられたように見える指数が返されます。最初は、printf を使用すれば数値が返されると思いましたが、数値がすでに切り上げられている場合は、期待どおりの値が返されません。
これは、区切られたデータファイルを読み取るために使用しているコードです。各レコードのデータは *. で区切られています。
export clm_total=$( awk -F* '{f1+=$3} END {print f1}' datafile.dat)
export new_clm_total=$(printf "%.2f" $clm_total)
スクリプトを実行すると、ログには次の内容が表示されます。
+ export clm_total=2.02822e+06
+ printf %.2f 2.02822e+06
+ export new_clm_total=2028220.00
+ echo 2028220.00
これはデータ ファイルのサンプルです。レコードは他にもありますが、193 レコードすべてを表示する必要はないと思います。
CLM*123456789*4820.9***13:A:1**A*Y*Y
CLM*123698547*3642.05***13:A:7**A*Y*Y
CLM*147852369*579.25***13:A:1**A*Y*Y
CLM*789654123*929.8***13:A:1**A*Y*Y
返される値は2028219.43と予想されますが、返される値は2.02822e+06です。これは2028220.00としてフォーマットされます。
答え1
awkは倍精度浮動小数点数で演算を実行します。小数点2桁の数値で正確な結果が得られる限界は正確にはわかりませんが、範囲内です。ただし、数字が大きくなれば問題になる可能性がある正確な結果を確実に得る必要がある場合は、整数に固執してオーバーフローに注意するか、bc
任意精度の演算を実行する を使用してください。
ここでの問題は、awk は正しい結果を計算しているが、デフォルトの印刷形式は近似値であるということです。結果を印刷するときは明示的な形式を使用してください。
export clm_total=$( awk -F'*' '{f1+=$3} END {printf "%.2f\n", f1}' datafile.dat)
あるいは、数値を文字列に変換するための印刷形式を変更すれば、 に固執することもできますprint
。デフォルトは で、%.6g
表示されている近似値が得られます。
export clm_total=$( awk -F'*' -v CONVFMT='%.2f' '{f1+=$3} END {print f1}' datafile.dat)
答え2
コンピュータ サイエンスで浮動小数点演算を扱う場合 (具体的にはawk
あなたの場合)、システム内でデータを表すために使用されている基礎となるメカニズムに注意を払う必要があります。
これがあなたの特定のケースで直面している問題だと思います。こちらの記事をご覧ください:D.3 浮動小数点数の注意点この問題に関する追加情報については、 を参照してください。この記事もこの問題を明らかにするのに役立ちました。15.2 浮動小数点プログラミングの理解。
私が知る限り、浮動小数点数を扱う場合、awk
数値の仮数部に使用できる桁数はわずかであるため、数値を蓄積し続けると、丸め誤差や切り捨て誤差が生じ、精度が失われることになります。
例
ここで、しきい値を超えて、科学的記数法を使用して実際の数値を追跡し始めるとわかります。
$ seq -f "%f" 1413 | awk '{f1+=$1+0.4} END {print f1}'
999556
$ seq -f "%f" 1414 | awk '{f1+=$1+0.4} END {print f1}'
1.00097e+06