0-6ren">
gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-12-04 14:15:55 24 4
gpt4 key购买 nike

当我对某些记录集执行求和时,我得到了奇怪的答案。
在一种情况下我没有使用 %d 而在下一种情况下我使用的是 %d

sum of using %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 +=$150} END {printf ("\n"sum)}' muar.txt
-|34

为什么它从 34 四舍五入到 33

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

根据测试评论的其他详细信息 - 您可以创建一个文件 let's 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" ,sum1,sum2
    printf "string conversion : "sum1" "sum2"\n"
    }'

    这导致以下输出(第一列 sum1 第二列 sum2
    full precision     :       34.0000000000000000      33.9999999999999787
    integer conversion : 34 33
    string conversion : 34 34

    为什么两次求和结果不同:

    本质上,这3个数字 1.95 , 0.970.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() 馈送它因此33.99999...转换为 33 .
  • printf ""sum2 ,这是从浮点数到字符串的转换。本质上,通过将字符串连接到数字,必须将数字转换为字符串。如果数字是纯整数,它只会将其转换为纯整数。然而,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" ,sum2,sum3
    printf "string conversion : "sum2" "sum3"\n"
    }'

    这导致以下输出(第一列 sum2 第二列 sum3)
    full precision     :       33.9999999999999787      34.0000000000000000
    integer conversion : 33 34
    string 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", sum2,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", sum2,sum3,c
    }
    }'

    关于awk - 使用 %d 在 Awk 程序中给出奇怪的舍入值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48808474/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com