AWK 中整数值的可移植范围?

AWK 中整数值的可移植范围?

根据第 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.6666661999998 = 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

相关内容