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

问题描述 投票:0回答:1

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

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

echo 210000000080401|awk -Wposix '{printf "%d %s\n", "0x" $1, $0}'
148618787703751680 210000000080401

正确的值为:148618787703751681

echo 210000000080401 | tr '[:lower:]' '[:upper:]'|xargs -i{} bash -c 'printf "%d\n" 0x{}'
148618787703751681

为什么 awk 没有给出正确的 o/p ?

shell awk
1个回答
3
投票

POSIX awk 规范没有定义如何将十六进制或八进制字符串转换为数字,仅定义十进制(例如,当

"10foo"+0
变为
10
时),因此不要添加
-Wposix
来告诉 gawk 禁用其扩展,然后请使用 gawk 人员为此目的提供的函数
strtonum()

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

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

*

awk 输出与问题中的 shell 输出不同,但是,这是由于 awk 中可用的浮点算术和/或精度影响了如此大的数字 - 你必须谷歌和/或阅读 gawk 手册,了解具体情况下发生的情况的准确解释,但如果您的 gawk 版本是为了支持非常大的数字处理而构建的(请参阅

https://www.gnu.org/software/gawk/manual/gawk. html#Arbitrary-Precision-Arithmetic
),那么你可以通过添加 1 参数来实现,然后你会得到你期望的输出:
-M

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

POSIX 规范
有一个部分说:

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

这意味着 POSIX awk 可能会也可能不会将十六进制常量(例如
*

)视为数字,但即使如此也不意味着将看起来像十六进制数字的字符串转换为数字,例如与

print 0x2f
,由 POSIX 定义。因此,这里有 POSIX 允许的一些有趣的行为(但由带有/不带
print ("0x2f" + 0)
的 GNU awk 演示,以展示不同的 awks 可以做什么):
由 POSIX 定义(因此任何 awk 都必须这样做才能兼容 POSIX)::

--posix

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

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

以下是 GNU awk(不带 
$ 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

)如何选择实现所需的功能,并且这样做是符合 POSIX 标准的,因为只有第一个是由 POSIX 定义的:

--posix

© www.soinside.com 2019 - 2024. All rights reserved.