awk 将非常大的数字从十六进制转换为十进制的输出不准确

Asi*_*ari 1 shell awk

我有以下数据:“210000000080401”。我需要将十六进制“210000000080401”转换为其十进制形式。

使用 Awk 没有给出正确的结果:

echo 210000000080401|awk -Wposix '{printf "%d %s\n", "0x" $1, $0}'
148618787703751680 210000000080401
Run Code Online (Sandbox Code Playgroud)

而正确的值为 148618787703751681

echo 210000000080401 | tr '[:lower:]' '[:upper:]'|xargs -i{} bash -c 'printf "%d\n" 0x{}'
148618787703751681
Run Code Online (Sandbox Code Playgroud)

为什么 awk 没有给出正确的输出?

Ed *_*ton 5

POSIX awk 规范没有定义如何将十六进制或八进制字符串转换为数字,仅定义十进制(例如,当"10foo"+0变为 时10),因此不要添加-Wposix告诉 gawk 禁用其扩展,然后使用strtonum()gawk 人们提供的函数该目的:

$ echo 210000000080401 | awk '{printf "%d %s\n", strtonum("0x" $1), $0}'
148618787703751680 210000000080401
Run Code Online (Sandbox Code Playgroud)

否则,您需要 POSIX 功能,但又依赖于 POSIX 未定义的行为*

不过,awk 输出与1问题中的 shell 输出不同,这是由于 awk 中可用的浮点算术和/或精度影响了如此大的数字 - 您必须谷歌和/或阅读 gawk 手册有关具体情况的准确解释,但如果您的 gawk 版本是为了支持非常大的数字处理而构建的(请参阅https://www.gnu.org/software/gawk/manual/gawk.html#Arbitrary-Precision -Arithmetic)然后你可以通过添加-M参数来做到这一点,然后你会得到你期望的输出:

$ echo 210000000080401 | awk -M '{printf "%d %s\n", strtonum("0x" $1), $0}'
148618787703751681 210000000080401
Run Code Online (Sandbox Code Playgroud)

* POSIX 规范中有一个部分说:

awk 的历史实现不会解析十六进制整数或浮点常量,如“0xa”和“0xap0”。由于疏忽,该标准的 2001 年至 2004 年版本要求支持十六进制浮点常量。这是由于对 atof() 的引用。此版本的标准允许但不要求实现使用 atof(),并包含如何将浮点数识别为匹配历史行为的替代方案的描述。此更改的目的是允许实现根据 ISO/IEC 9899:1990 标准或 ISO/IEC 9899:1999 标准识别浮点常量,并允许(但不要求)实现识别十六进制整数常量。

这意味着 POSIX awk 可能会或可能不会考虑十六进制常量,例如print 0x2f数字,但即使如此,也不意味着将看起来像十六进制数字的字符串转换为数字,例如 with print ("0x2f" + 0),是由 POSIX 定义的。下面是 POSIX 允许的一些有趣的行为(但由 GNU awk 演示,带/不带--posix显示不同的 awks 可以做什么):

由 POSIX 定义(因此任何 awk 都必须这样做才能符合 POSIX)::

$ echo '0x2f' | awk --posix '{print $0}'
0x2f
$ echo '0x2f' | awk '{print $0}'
0x2f
Run Code Online (Sandbox Code Playgroud)

POSIX 未定义(因此任何 awk 都可以执行其中一项或任何其他操作,并且仍然符合 POSIX 标准):

$ echo '0x2f' | awk --posix '{print $0+0}'
47
$ echo '0x2f' | awk '{print $0+0}'
0

$ awk --posix 'BEGIN{print 0x2f}'
0
$ awk 'BEGIN{print 0x2f}'
47
Run Code Online (Sandbox Code Playgroud)

以下是 GNU awk(不带--posix)如何选择实现所需的功能,并且这样做是符合 POSIX 标准的,因为只有第一个是由 POSIX 定义的:

$ echo '0x2f' | awk '{print $0}'
0x2f
$ echo '0x2f' | awk '{print $0+0}'
0
$ echo '0x2f' | awk '{print strtonum($0)}'
47
$ echo '0x2f' | awk '{print 0x2f}'
47
Run Code Online (Sandbox Code Playgroud)