我正在使用 awk 进行一些基本计算,并希望强制我的输出采用科学计数法。我正在用它OFMT="%.15e"来完成这个任务。在大多数机器上,我得到了预期的输出:
$ awk 'BEGIN { OFMT = "%.15e"; print 4.483923595133619e+29 / 1000 }'
4.483923595133619e+26
Run Code Online (Sandbox Code Playgroud)
但是我的集群上的 awk 版本给出了:
$ awk --version | head --lines=2
GNU Awk 4.0.2
Copyright (C) 1989, 1991-2012 Free Software Foundation.
$ awk 'BEGIN { OFMT = "%.15e"; print 4.483923595133619e+29 / 1000 }'
448392359513361882871234560
Run Code Online (Sandbox Code Playgroud)
为什么 awk 的这个版本/配置没有按照要求输出为科学计数法?我怎样才能便携地得到我想要的结果( 4.483923595133619e+26 )?
我相信您已经发现了 GNU awk 中长期存在的错误。
GNU awk 行为正确。
POSIX是这样说的:
完全等于整数值的数值(请参阅 ISO C 标准派生的概念)应通过等效于以字符串作为参数调用函数
sprintf(请参阅字符串函数)转换为字符串"%d",fmt并且被转换为第一个也是唯一一个参数的数值expr。
使用printf而不是设置OFMT并不是解决 gawk bug 的方法。这是代码中错误的解决方案。
任何足够大的浮点值都完全等于整数。
(如果您习惯于具有不同整数和浮点类型的语言,awk 通过值区分整数和非整数的方式可能会有点令人困惑。)
这是正在发生的事情的演示:
$ cat foo.awk
#!/usr/bin/awk -f
BEGIN {
OFMT="%.16e"
for (i = 50; i <= 55; i ++) {
x = 2 ** i - 0.5
printf("2**%d - 0.5 = %.3f = ", i, x)
print(x)
}
}
$ ./foo.awk
2**50 - 0.5 = 1125899906842623.500 = 1.1258999068426235e+15
2**51 - 0.5 = 2251799813685247.500 = 2.2517998136852475e+15
2**52 - 0.5 = 4503599627370495.500 = 4.5035996273704955e+15
2**53 - 0.5 = 9007199254740992.000 = 9007199254740992
2**54 - 0.5 = 18014398509481984.000 = 18014398509481984
2**55 - 0.5 = 36028797018963968.000 = 36028797018963968
$
Run Code Online (Sandbox Code Playgroud)
我最初认为这是 gawk 中的一个错误,我在这里提交了一个错误报告:
https://lists.gnu.org/archive/html/bug-gawk/2023-05/msg00010.html
Andrew J. Schorr 的回复正确地表明这是预期的行为。
https://lists.gnu.org/archive/html/bug-gawk/2023-05/msg00011.html
邮件列表上有更多回复。该文档可能对OFMT和之间的关系不够清楚CONVFMT。我上面引用的 POSIX 措辞是在谈论CONVFMT.
https://lists.gnu.org/archive/html/bug-gawk/2023-05/threads.html