AWKの整数値の移植可能範囲は何ですか?

AWKの整数値の移植可能範囲は何ですか?

39ページのセクション3.2.9に従うコンパチブル・シルスクリーフティング区(第5版)、互換性のあるシェルスクリプトを作成する方法については、限られた整数範囲を持ついくつかのAWK実装があります(私の翻訳)。

たとえば、AWKに次の出力が表示されませんか?

$ awk 'BEGIN{print 2147483648}'
2.14748e+09
$

これは、0x7FFFFFFF(4バイト符号付き整数の最大値)以上の整数を処理できない実装です。これが、数桁の整数を扱うときに注意しなければならない理由です。計算せずに表示したい場合は、文字列として扱う必要があります。

しかし、ポータブルスコープが正確に何であるかを本で見たことはありません。それで、POSIXのドキュメントをチェックして見つけました。SUSv2範囲が指定されていないようです。次のように言います。

値が大きすぎるか小さすぎて表現できない場合、動作は定義されません。

存在する2004年版、それ〜らしい整数および浮動小数点値はそれぞれ long および double 型で符号が付けられます ("..." はその部分を省略したことを意味します)。

整数変数と定数は、ISO C標準の符号付き長いデータ型と同じように実装する必要があります。浮動小数点はISO C標準二重タイプと同じように実装する必要があります。

これは[-2147483647、+2147483647](PS. Wikipediaで範囲を見つけました)が浮動小数点として扱われない移植可能な整数範囲であることを意味しますか?

答え1

数値を浮動小数点数ではなく整数として扱うことが正確に何を意味するのか疑問に思います。

出力される内容を意味するなら、printf "%d"gawk、mawk、およびBusyboxでは-2147483647が安全なように見えます。下の数字は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とにかく、たとえば、%.0fmawkで数字全体を印刷するように変更することもできます。

$ 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

もちろん、計算は関係なく浮動小数点数を使用して実行されるため、intで切り捨てられない限り、ここではをOFMT取得します。2000000 = 3 * 666666.6666661999998 = 3 * 666666

$ 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完全に処理できます。唯一の注意点は、9ビットより長い整数の代わりに使用することです。IEEE 754awk%.f%d %i %u

 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^3352-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

exprがホスト上で正確な整数として表示できない場合は、exprをsprintf(CONVFMT、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整数として出力され、残りの2つは浮動小数点数として出力されます。

GNUではこの問題は発生awkしません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形式を使用しません。制限を明らかにすることは、1つを追加すると9007199254740992値が再び繰り返されることです。値は53ビットに切り捨てられます。

$ awk 'BEGIN{ CONVFMT="%.2f"; val=2^53; print val-1"",val"",val+1""}'
9007199254740991 9007199254740992 9007199254740992

GMPライブラリ

GNU awkがGMPおよびFMPRライブラリ(現在の標準)にコンパイルされると、すべての(適切な範囲内で)整数が整数として表示されます。

$ 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

関連情報