awk高精度算术


11

我正在寻找一种方法来告诉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

关于如何获得所需精度的任何建议?


也许awk具有更高的分辨率,但这只是您的输出格式被截断了。使用printf。
dubiousjim 2012年

使用printf后结果值无变化。问题已相应编辑。
mkc 2012年

正如@manatwork指出的那样,这gsub是不必要的。问题是gsub对字符串有效,而不是数字有效,因此首先使用进行转换CONVFMT,其默认值为%.6g
2012年

@ jw013,正如我在问题中提到的那样,我最初的问题需要gsub,因为我需要用1%的增量替换数字。同意,在简化示例中,它不是必需的。
mkc 2012年

Answers:


12
$ 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

如果要任意精度,AWK可以使用该-M标志并将其PREC值设置为大数
Robert Benson

3
@RobertBenson,仅适用于GNU awk,仅适用于最新版本(4.1或更高版本,因此在编写答案时不适用),并且仅当在编译时启用MPFR时才适用。
斯特凡Chazelas

2

为了使用(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位精度,并且即使在很多时候看起来正确的是17位,您也只能确保15位正确的十进制数字。那是海市rage楼。

$ 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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.