Awk
awk 高精度算術
我正在尋找一種方法來告訴 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