使用%d会在Awk程序中给出奇怪的舍入值

使用%d会在Awk程序中给出奇怪的舍入值,第1张

概述当我在某些记录集上执行求和时,我得到了奇怪的答案. 在一种情况下,我没有使用%d,在下一种情况下我使用的是%d 使用%d之和的第一个表达式 awk -F"|" '(NR > 0 && NR < 36) {sum +=$150} END {printf ("%d\n",sum)}' muar.txt-|33 没有%d awk -F"|" '(NR > 0 && NR < 36) {sum +=$1 当我在某些记录集上执行求和时,我得到了奇怪的答案.
在一种情况下,我没有使用%d,在下一种情况下我使用的是%d

使用%d之和的第一个表达式

awk -F"|" '(NR > 0 && NR < 36) {sum +=0} END {printf ("%d\n",sum)}' muar.txt-|33

没有%d

awk -F"|" '(NR > 0 && NR < 36) {sum +=0} END {printf ("\n"sum)}' muar.txt-|34

为什么它从34变为33

只是添加更多信息,直到34行我得到的总和为33.03而第35行的值为0.97所以实际上它应该是34而不是33

根据测试评论的附加细节 – 您可以创建一个文件let a.txt
只有一个领域.第一个值是空白第二个是1.95然后连续18次097,然后是0.98然后是6次0.97然后是0.98然后是3次0.97然后是0.98 2次然后是2次0.97

或者,您可以连续使用1.95 – 1次,0.97 – 29次和0.98次4次

解决方法 你的问题的答案有两个:

>有一个数字问题
> awk进行一些内部转换

你的一个例子是:1.95 29 * 0.97 4 * 0.98.我们都同意这个值的总和是34.下面的小’awk程序,以两种不同的方式进行计算,从而产生显着的结果:

awk 'BEGIN{sum1=1.95 + 29*0.97 + 4*0.98           sum2=1.95;           for(i=1;i<=29;i++){sum2+=0.97};           for(i=1;i<=4;i++) {sum2+=0.98};           printf "full precision     : %25.16f%25.16f\n",sum1,sum2           printf "integer conversion : %25d%25d\n",sum2           printf "string conversion  : "sum1" "sum2"\n"}'

这导致以下输出(第一列sum1第二列sum2

full precision     :       34.0000000000000000      33.9999999999999787integer conversion :                        34                       33string conversion  : 34 34

为什么这两个总和有不同的结果:

实质上,3个数字1.95,0.97和0.98不能以二进制格式表示.发生近似表示它们

1.95 ~ 1.94999999999999995559107901499...0.97 ~ 0.96999999999999997335464740899...0.98 ~ 0.97999999999999998223643160599...

当按照sum2进行求和时,33次加法的误差会增加并导致最终结果:

sum2 = 33.99999999999997868371792719699...

sum1上的误差远小于sum2,因为我们只进行了2次乘法和2次加法.事实上,错误蒸发到正确的结果(即误差小于10 ^ -17):

1.95 ~  1.94999999999999995559107901499...29*0.97 ~ 28.12999999999999900524016993586... 4*0.98 ~  3.91999999999999992894572642399...   sum1 ~ 34.00000000000000000000000000000...

有关上述内容的详细说明,请参阅强制性条款What Every Computer Scientist Should Know About Floating-Point Arithmetic

打印声明发生了什么?

awk基本上是在进行内部转换:

> printf“%d”请求一个整数,但它是一个浮点数. awk正在接收sum2并通过删除数字的小数部分将其转换为整数,或者您可以想象它通过int()将其转换为3399999 …转换为33.
> printf“”sum2,这是从float到字符串的转换.基本上通过将字符串连接到数字,数字必须以字符串形式转换.如果数字是纯整数,它只会将其转换为纯整数.但是,sum2是一个浮点数.

sum2到字符串的转换是用sprintf(CONVFMT,sum2)在内部完成的,其中CONVFMT是一个awk内置变量,设置为%.6g.因此,sum2默认为舍入,以最多6个十进制数字表示.因此“”sum2 – > “34”.

我们可以改进sum2:

是! sum2只不过是我们想要添加的一系列数字的表示.首先搜索所有常用术语并使用sum1中的使用乘法是不切实际的.使用Kahan Summation可以实现改进.其背后的想法是跟踪代表您丢失的数字的补偿项.

以下程序演示了它:

awk 'BEGIN{sum2=1.95;           for(i=1;i<=29;i++){sum2+=0.97};           for(i=1;i<=4;i++) {sum2+=0.98};           sum3=1.95; c=0           for(i=1;i<=29;i++) { y = 0.97 - c; t = sum3 + y; c = (t - sum3) - y; sum3 = t }           for(i=1;i<=4;i++)  { y = 0.98 - c; t = sum3 + y; c = (t - sum3) - y; sum3 = t }           printf "full precision     : %25.16f%25.16f\n",sum2,sum3           printf "integer conversion : %25d%25d\n",sum3           printf "string conversion  : "sum2" "sum3"\n"}'

这导致以下输出(第一列sum2第二列sum3)

full precision     :       33.9999999999999787      34.0000000000000000integer conversion :                        33                       34string conversion  : 34 34

如果要查看中间步骤以及sum2和sum3之间的差异,可以查看以下代码.

awk 'BEGIN{ sum2=sum3=1.95;c=0;             for(i=1;i<=29;i++) {                sum2+=0.97;                y = 0.97 - c; t = sum3 + y; c = (t - sum3) - y; sum3 = t;                printf "%25.16f%25.16f%25.16e\n",sum3,c             }             for(i=1;i<=4;i++) {                sum2+=0.98;                y = 0.98 - c; t = sum3 + y; c = (t - sum3) - y; sum3 = t;                printf "%25.16f%25.16f%25.16e\n",c             }      }'
总结

以上是内存溢出为你收集整理的使用%d会在Awk程序中给出奇怪的舍入值全部内容,希望文章能够帮你解决使用%d会在Awk程序中给出奇怪的舍入值所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://www.outofmemory.cn/yw/1027417.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-23
下一篇 2022-05-23

发表评论

登录后才能评论

评论列表(0条)

保存