![AWK 中整数值的可移植范围?](https://linux22.com/image/189795/AWK%20%E4%B8%AD%E6%95%B4%E6%95%B0%E5%80%BC%E7%9A%84%E5%8F%AF%E7%A7%BB%E6%A4%8D%E8%8C%83%E5%9B%B4%EF%BC%9F.png)
根据第 39 页第 3.2.9 节コンパチブル・シェルsukuriputingu(第5版),一本关于如何制作兼容的shell脚本的参考书,有一些整数范围有限的AWK实现(我翻译的):
例如,您的 AWK 不会显示以下输出吗?
$ awk 'BEGIN{print 2147483648}' 2.14748e+09 $
这是一个无法处理超过 0x7FFFFFFF(4 字节有符号整数的最大值)的整数的实现。这就是为什么在处理多位整数时应该小心。如果您只想显示它们而不进行计算,则应该将其视为字符串。
但我从来没有在书上找到便携式范围到底是什么。所以我查阅了 POSIX 文档来了解SUSv2似乎没有具体说明范围,只是说:
如果该值太大或太小而无法表示,则行为未定义。
在2004年版, 它似乎整数和浮点值分别是有符号长类型和双类型(“...”表示我省略了该部分):
整数变量和常量...应等效于 ISO C 标准有符号长数据类型来实现;浮点应以等同于 ISO C 标准双精度类型的方式实现。
这是否意味着 [-2147483647,+2147483647] (PS。我在维基百科上查找了范围)是不会被处理为浮点的整数的可移植范围?
答案1
我想知道您将数字作为整数而不是浮点数处理到底是什么意思。
如果你的意思printf "%d"
是输出什么,那么看起来 -2147483647 在 gawk、mawk 和 Busybox 中是安全的。低于该值的数字在 mawk 中打印为 -2147483647,在 Busybox 中打印为 -2147483648,但 gawk 以及无论我的 Mac 上的 awk 是什么,都会打印实际值。
另一方面,如果您的意思是用数字进行计算,那么您可能可以获得更大的范围。 awk 应该使用任何“ISO C标准双型”是在平台上。最常见的是 IEEE 754 双精度浮点数,但这不是必需的。
对于 IEEE double,尾数为 52+1 位,因此大约 ± 2 53范围内的任何整数都应该可以准确表示。打印数字只是输出格式的问题。
print
( )的默认输出格式OFMT
是%.6g
,这意味着打印 6 位有效数字。但这并不是事实的全部,因为整数应该被打印为整数,但这取决于 awk 的版本,它们在这里算作整数。有些将其限制为特定范围内的数字,例如:
$ busybox awk 'BEGIN { a = 9007199254740992; print a; printf OFMT "\n", a }'
9007199254740992
9.0072e+15
大众。
$ mawk 'BEGIN { a = 9007199254740992;
print a; printf OFMT "\n", a }'
9.0072e+15
9.0072e+15
无论如何,您应该能够更改OFMT
为 例如%.0f
让 mawk 也打印完整的数字:
$ mawk 'BEGIN { OFMT="%.0f"; a = 9007199254740992;
print a; printf OFMT "\n", a }'
9007199254740992
9007199254740992
大于 ± 2 53,并且您会遇到问题,因为最低位开始下降:
$ awk 'BEGIN { OFMT="%.0f"; a=9007199254740990;
for (i = 0; i < 6; i++) print a, "+", i, "=", a + i; }'
9007199254740990 + 0 = 9007199254740990
9007199254740990 + 1 = 9007199254740991
9007199254740990 + 2 = 9007199254740992
9007199254740990 + 3 = 9007199254740992
9007199254740990 + 4 = 9007199254740994
9007199254740990 + 5 = 9007199254740996
当然,计算仍然使用浮点数完成,无论OFMT
,所以这里你得到2000000 = 3 * 666666.666666
,1999998 = 3 * 666666
除非你截断为 int :
$ awk 'BEGIN{a = 2000000; b = a/3; print 3*b}'
2000000
$ awk 'BEGIN{a = 2000000; b = int(a/3); print 3*b}'
1999998
您可能应该制作一个测试脚本来验证您使用的 awk 所需的行为。
答案2
只是想快速消除一个相当常见的误解mawk
:它完全能够处理IEEE 754
双精度浮点,就像任何其他浮点一样awk
- 唯一需要注意的是使用%.f
而不是%d %i %u
任何长度超过 9 位的整数:
jot -s $'\n ' -w '%2d' - 1 33 2 | mawk 'BEGIN { printf("\n ") _ += __=_^=FS="^$" ___ = __-(++_)^-(_^_+_+_) OFS = "-st/nd/rd/th-power-of-3 :: " OFMT = CONVFMT = "<( %\047"(_^_)".f )>" } $++NF = _^$__ * ___^(+$__<_^_)'
1-st/nd/rd/th-power-of-3 :: <( 3 )>
3-st/nd/rd/th-power-of-3 :: <( 27 )>
5-st/nd/rd/th-power-of-3 :: <( 243 )>
7-st/nd/rd/th-power-of-3 :: <( 2,187 )>
9-st/nd/rd/th-power-of-3 :: <( 19,683 )>
11-st/nd/rd/th-power-of-3 :: <( 177,147 )>
13-st/nd/rd/th-power-of-3 :: <( 1,594,323 )>
15-st/nd/rd/th-power-of-3 :: <( 14,348,907 )>
17-st/nd/rd/th-power-of-3 :: <( 129,140,163 )>
19-st/nd/rd/th-power-of-3 :: <( 1,162,261,467 )>
21-st/nd/rd/th-power-of-3 :: <( 10,460,353,203 )>
23-st/nd/rd/th-power-of-3 :: <( 94,143,178,827 )>
25-st/nd/rd/th-power-of-3 :: <( 847,288,609,443 )>
27-st/nd/rd/th-power-of-3 :: <( 7,625,597,484,987 )>
29-st/nd/rd/th-power-of-3 :: <( 68,630,377,364,883 )>
31-st/nd/rd/th-power-of-3 :: <( 617,673,396,283,947 )>
33-st/nd/rd/th-power-of-3 :: <( 5,559,060,566,555,523 )>
最后一个数字3^33
位于52-
和53-bits
(~ ) 之间,是全精度52.304 bits
支持的最大 3 次方。754 double fp
2^53 - 1
除了所有双精度浮点软件遇到的相同限制之外,它不仅没有关于整数范围的定制问题,
它还能够直接将精美的格式化语法添加到CONVFMT
/中OFMT
,从而允许直接生成输出,而无需sprintf()/printf()
对每行进行额外的调用。
mawk 1.3.4
这是广泛分发的标准,而不是自定义编译:
mawk -Wv
mawk 1.3.4 20200120
Copyright 2008-2019,2020, Thomas E. Dickey
Copyright 1991-1996,2014, Michael D. Brennan
random-funcs: unknown
regex-funcs: internal
compiled limits:
sprintf buffer 8192
maximum-integer 2147483647
答案3
事实上,有三AWK 中的特定限制。
AWK 整数
如果用整数,他们的意思是这样的描述:
AWK 中值的内部表示始终是浮点型(通常是双浮点型)。当使用值生成字符串时,如果该值是整数,则%d
使用 的格式(无论是什么CONVFMT
。因此:整数在打印为文本时将保持整数。
从手册中摘录mawk
:
通过用 sprintf(CONVFMT, expr) 替换 expr 将数值表达式转换为字符串,除非 expr 可以在主机上表示为精确整数,然后将其转换为 sprintf("%d", expr)。
在实践中,这可以通过以下方式揭示:
$ mawk 'BEGIN{ CONVFMT="used" ; a=12 ; b = 2^35; c = a "" ; d = b ""; print c, d }'
12 used
或者,以更常见的方式:
$ mawk 'BEGIN{ CONVFMT="%2.2f" ; a=2^12 ; b = 2^35; c = a "" ; d = b ""; print c, d }'
4096 34359738368.00
$ mawk 'BEGIN{ CONVFMT="%2.2f" ; a=2^31 ; print a-1"",a"",a+1"" }'
2147483647 2147483648.00 2147483649.00
如上所示, 的值2^31-1
打印为整数,其他两个打印为浮点数。
GNUawk
不会busybox awk
显示该问题(至少在 64 位 Debian 中)。
漂浮
浮点数受到可以使用的二进制位数的限制。这不是它们如何格式化的问题,而是它们是什么的问题。
在 mawk 中,任何大于的值2^31
都将打印为浮点数(使用 CONVFMT="%.2f")。
$ mawk 'BEGIN{ CONVFMT="%.2f"; val=2^53; print val-1"",val"",val+1""}'
9007199254740991.00 9007199254740992.00 9007199254740992.00
但事实证明,最大的整数(不带指数)是2^53
。 GNU awk 中也是如此,但 GNU awk 不使用该%.2f
格式。揭示极限的是加一会9007199254740992
再次重复该值。该值被截断为 53 位。
$ awk 'BEGIN{ CONVFMT="%.2f"; val=2^53; print val-1"",val"",val+1""}'
9007199254740991 9007199254740992 9007199254740992
GMP库
当使用 GMP 和 FMPR 库(现在是标准)编译 GNU awk 时,所有(在合理范围内)整数都表示为整数:
$ awk -M 'BEGIN{ print 2^300; print 2^300+1}'
2037035976334486086268445688409378161051468393665936250636140449354381299763336706183397376
2037035976334486086268445688409378161051468393665936250636140449354381299763336706183397377
在这种情况下,限制相当大(我没有搜索特定的内存限制,但请确保存在一个。无限的数字是无法表示的)。
答案4
mawk 'BEGIN { OFS="\t"; OFMT="%\44725.f" ____=___=_*=((_+=_^=_<_)^++_)^(_*_++)*(_______=_) ____*=(_=_______)^(_+_+_) _________=(_______*=_*_)^(++_+—_);—____ for(______+=______=(_+=(_^=_<_)+_)^(_+_+_+_);_<______;_++) { for(__+=__^=_<_;__<_______;__++) { if( (________=_^__)<____ && ___<________ ) { print "",_,__,________ } } } }' | mawk '+(/\t2\t/)<+(/[17]$/)' FS='^$' | mawk '!(NR % 1777)' FS='^$'
2719 4 54,655,872,347,521
5681 3 183,347,236,241
8641 4 5,575,143,118,268,161
15323 3 3,597,753,503,267
24211 3 14,191,822,905,931
33093 3 36,241,688,055,357
41981 3 73,987,497,479,141
50863 3 131,584,858,085,647
59751 3 213,321,944,741,751
68633 3 323,294,970,192,137
77521 3 465,862,871,291,761
86403 3 645,039,730,972,827
95291 3 865,277,983,727,171
104173 3 1,130,486,847,025,717
113061 3 1,445,234,988,645,981
121943 3 1,813,304,024,948,807
130831 3 2,239,401,592,646,191
139713 3 2,727,158,971,340,097
148601 3 3,281,445,502,325,801
157483 3 3,905,719,392,797,587
166371 3 4,605,034,424,282,811
175253 3 5,382,652,995,919,277
184141 3 6,243,836,065,115,221
193023 3 7,191,627,487,303,167
201911 3 8,231,518,131,421,031
您可以自己复制代码运行。
从字面上看,我设置的唯一非默认值是使用\t
制表符作为输出分隔符,以及逗号对齐的输出格式。
我什至没有sprintf()/printf()
在代码中的任何地方调用过一次,也没有访问过外部实用程序。
mawk-1
我和其他人一样:
mawk 1.3.4 20200120
Copyright 2008-2019,2020, Thomas E. Dickey
Copyright 1991-1996,2014, Michael D. Brennan
random-funcs: unknown
regex-funcs: internal
compiled limits:
sprintf buffer 8192
maximum-integer 2147483647