![Shell 脚本和在分隔文件中添加大值问题](https://linux22.com/image/51341/Shell%20%E8%84%9A%E6%9C%AC%E5%92%8C%E5%9C%A8%E5%88%86%E9%9A%94%E6%96%87%E4%BB%B6%E4%B8%AD%E6%B7%BB%E5%8A%A0%E5%A4%A7%E5%80%BC%E9%97%AE%E9%A2%98.png)
我有一个脚本,它读取分隔文件并为每个记录添加文件中的第三个元素。对于大多数数据文件来说,除了一个数据文件之外,这种方法都可以正常工作。我有一个数据文件,其中有 193 条记录。我期望从脚本中返回 2028219.43 。相反,我得到了一个指数数,它似乎已经被四舍五入了。起初我认为通过使用 printf 我会得到数字,但如果数字已经四舍五入,那么它不会给我返回我所期望的结果。
这是我用来读取分隔数据文件的代码。每条记录中的数据由 * 分隔:
export clm_total=$( awk -F* '{f1+=$3} END {print f1}' datafile.dat)
export new_clm_total=$(printf "%.2f" $clm_total)
这是我运行脚本时日志中显示的内容:
+ export clm_total=2.02822e+06
+ printf %.2f 2.02822e+06
+ export new_clm_total=2028220.00
+ echo 2028220.00
这是数据文件的示例。记录还多,我觉得没有必要把193条记录全部显示出来:
CLM*123456789*4820.9***13:A:1**A*Y*Y
CLM*123698547*3642.05***13:A:7**A*Y*Y
CLM*147852369*579.25***13:A:1**A*Y*Y
CLM*789654123*929.8***13:A:1**A*Y*Y
我期待返回的是 2028219.43 我返回的是 2.02822e+06 然后将其格式化为 2028220.00
答案1
awk 对双精度浮点数执行算术运算。我不知道你能得到两位小数的精确结果的确切限制是多少,但你在范围内。然而这如果数字变大可能会出现问题。如果您需要确保获得准确的结果,要么坚持使用整数并注意溢出,要么使用bc
执行任意精度算术的函数。
这里的问题是 awk 正在计算正确的结果,但默认的打印格式是近似的。打印结果时使用明确的格式。
export clm_total=$( awk -F'*' '{f1+=$3} END {printf "%.2f\n", f1}' datafile.dat)
或者,您也可以坚持print
更改打印格式以将数字转换为字符串。默认值%.6g
会产生您所看到的近似值。
export clm_total=$( awk -F'*' -v CONVFMT='%.2f' '{f1+=$3} END {print f1}' datafile.dat)
答案2
在处理计算机科学中的浮点数学时(特别是awk
在您的情况下),您必须注意用于表示系统内数据的底层机制。
我相信这是您在特定情况下面临的问题。参见这篇文章:D.3 浮点数注意事项,了解有关该主题的更多信息。这篇文章也有助于阐明这个问题:15.2 理解浮点编程。
我可以说,在处理浮点数时,awk
您似乎只有几个数字可用于数字的尾数部分,因此,当您继续累积数字时,您会遇到引入舍入和截断错误的情况,并且你正在失去准确性。
例子
您可以在此处看到我们何时超过阈值并开始使用科学记数法来跟踪实际数字。
$ seq -f "%f" 1413 | awk '{f1+=$1+0.4} END {print f1}'
999556
$ seq -f "%f" 1414 | awk '{f1+=$1+0.4} END {print f1}'
1.00097e+06