Awk

awk 高精度算術

  • September 24, 2020

我正在尋找一種方法來告訴 awk 在替換操作中進行高精度算術。這涉及從文件中讀取一個欄位並用該值的 1% 增量替換它。但是,我在那裡失去了精確度。這是問題的簡化再現:

$ echo 0.4970436865354813 | awk '{gsub($1, $1*1.1)}; {print}'
  0.546748

在這裡,我有一個十進制精度後的 16 位,但 awk 只給出了六位。使用 printf,我得到了相同的結果:

$ echo 0.4970436865354813 | awk '{gsub($1, $1*1.1)}; {printf("%.16G\n", $1)}'
0.546748

關於如何獲得所需精度的任何建議?

$ 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

要使用 (GNU) awk(編譯了 bignum)獲得更高的精度,請使用:

$ echo '0.4970436865354813' | awk -M -v PREC=100 '{printf("%.18f\n", $1)}'
0.497043686535481300

PREC=100 表示 100 位而不是預設的 53 位。

如果該 awk 不可用,請使用 bc

$ echo '0.4970436865354813*1.1' | bc -l
.54674805518902943

或者你需要學會忍受浮點數固有的不精確性。


在您的原始行中有幾個問題:

  • 1.1 的係數是增加 10%,而不是 1%(應該是 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 

但是不要以為這意味著更高的精度。內部數字表示仍然是雙倍大小的浮點數。這意味著 53 位精度,因此您只能確定 15 個正確的十進制數字,即使很多時候最多 17 個數字看起來是正確的。那是海市蜃樓。

$ 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

引用自:https://unix.stackexchange.com/questions/57006