代替操作中にawkに高精度演算を実行するように指示する方法を探しています。これには、ファイルからフィールドを読み取って値を1%増分に置き換えることが含まれます。しかし、私はそこで精度を失っています。以下は、問題を単純化して再現したものです。
$ echo 0.4970436865354813 | awk '{gsub($1, $1*1.1)}; {print}'
0.546748
ここでは小数点以下16桁の精度がありますが、awkは6桁のみ提供します。 printfを使用すると、同じ結果が得られます。
$ echo 0.4970436865354813 | awk '{gsub($1, $1*1.1)}; {printf("%.16G\n", $1)}'
0.546748
必要な精度を得る方法に関する提案はありますか?
答え1
$ echo 0.4970436865354813 | awk -v CONVFMT=%.17g '{gsub($1, $1*1.1)}; {print}'
0.54674805518902947
またはここでより良いです:
$ echo 0.4970436865354813 | awk '{printf "%.17g\n", $1*1.1}'
0.54674805518902947
おそらくあなたが達成できる最高の目標でしょう。任意の精度のためにbc
。
$ echo '0.4970436865354813 * 1.1' | bc -l
.54674805518902943
答え2
(GNU)awk(bignumでコンパイル)を使用してより高い精度を得るには、次のようにします。
$ echo '0.4970436865354813' | awk -M -v PREC=100 '{printf("%.18f\n", $1)}'
0.497043686535481300
PREC = 100は、デフォルトの53ビットではなく100ビットを意味します。
awkが利用できない場合は、bcを使用してください。
$ echo '0.4970436865354813*1.1' | bc -l
.54674805518902943
あるいは、ワゴンの本質的な不正確さを取る方法を学ぶ必要があります。
元の行にはいくつかの問題があります。
- 1.1倍は1%ではなく10%増加を意味します(1.01倍でなければなりません)。私は10%を使います。
文字列を(浮動小数点)数値に変換する形式は、CONVFMTによって提供されます。デフォルト値はです
%.6g
。これは値を小数点以下6桁(ドットの後ろ)に制限します。これはgsub変更の結果に適用されます$1
。$ a='0.4970436865354813' $ echo "$a" | awk '{printf("%.16f\n", $1*1.1)}' 0.5467480551890295 $ echo "$a" | awk '{gsub($1, $1*1.1)}; {printf("%.16f\n", $1)}' 0.5467480000000000
printf 形式は
g
末尾のゼロを削除します。$ echo "$a" | awk '{gsub($1, $1*1.1)}; {printf("%.16g\n", $1)}' 0.546748 $ echo "$a" | awk '{gsub($1, $1*1.1)}; {printf("%.17g\n", $1)}' 0.54674800000000001
どちらの問題も、次の方法で解決できます。
$ echo "$a" | awk '{printf("%.17g\n", $1*1.1)}' 0.54674805518902947
または
$ echo "$a" | awk -v CONVFMT=%.30g '{gsub($1, $1*1.1)}; {printf("%.17f\n", $1)}' 0.54674805518902947
しかし、これがより高い精度を意味するとは思わないでください。内部数値表現は依然として2倍のサイズの浮動小数点数です。これは53桁の精度を意味するため、最大17桁まで数回正確に見えても、正しい10進数は15桁しか決定できません。蜃気楼です。
$ echo "$a" | awk -v CONVFMT=%.30g '{gsub($1, $1*1.1}; {printf("%.30f\n", $1)}'
0.546748055189029469325134868996
正しい値は次のとおりです。
$ echo "scale=18; 0.4970436865354813 * 1.1" | bc
.54674805518902943
bignumライブラリが次のようにコンパイルされている場合は、計算に(GNU)awkを使用することもできます。
$ echo "$a" | awk -M -v PREC=100 -v CONVFMT=%.30g '{printf("%.30f\n", $1)}'
0.497043686535481300000000000000
答え3
私のawkスクリプトは1行以上のコードなので、Stéphane ChazelasとIsaacの答えを組み合わせました。
CONVFMT
出力形式を処理するためにグローバル変数を設定しました。- 私はbignumパラメータ
-M
とPREC
変数も使用します。
サンプルスニペット:
#!/usr/bin/awk -M -f
BEGIN {
FS="<|>"
CONVFMT="%.18g"
PREC=100
}
{
if ($2 == "LatitudeDegrees") {
CORR = $3 // redacted specific corrections
print(" <LatitudeDegrees>" CORR "</LatitudeDegrees>");
} else if ($2 == "LongitudeDegrees") {
CORR = $3 // redacted specific corrections
print(" <LongitudeDegrees>" CORR "</LongitudeDegrees>");
} else {
print($0);
}
}
END {
}
OPは彼の例を単純化したが、awkスクリプトが1行スクリプトではない場合、sで汚染したくないので、変数でprintf
このように書式設定します。精度は同じなので、実際のコマンドライン呼び出しで失われることはありません。