bash
シェルの算術評価機能には制限が設定されています。マニュアルはシェル演算のこの側面については簡潔ですが、次のように述べています:
評価はオーバーフローのチェックなしで固定幅整数で行われますが、0による除算はトラップされ、エラーとしてフラグが立てられます。演算子とその優先順位、結合性、および値は、C言語の場合と同じです。
これが参照する固定幅整数は、実際にはどのデータ型が使用されているか(そしてこれがこれを超えている理由の詳細)ですが、制限値は次のように表されます/usr/include/limits.h
。
# if __WORDSIZE == 64
# define ULONG_MAX 18446744073709551615UL
# ifdef __USE_ISOC99
# define LLONG_MAX 9223372036854775807LL
# define ULLONG_MAX 18446744073709551615ULL
そして、それを知ったら、次のようにこの事実の状態を確認できます。
# getconf -a | grep 'long'
LONG_BIT 64
ULONG_MAX 18446744073709551615
これは64ビット整数であり、これは算術評価のコンテキストでシェルで直接変換されます。
# echo $(((2**63)-1)); echo $((2**63)); echo $(((2**63)+1)); echo $((2**64))
9223372036854775807 //the practical usable limit for your everyday use
-9223372036854775808 //you're that much "away" from 2^64
-9223372036854775807
0
# echo $((9223372036854775808+9223372036854775807))
-1
したがって、2 63と2 64 -1の間では、ULONG_MAXからの距離が1であることを示す負の整数が得られます。評価がその制限に達してオーバーフローすると、どのような順序でも警告は表示されず、評価のその部分は0にリセットされます。これにより、たとえば右結合指数などの異常な動作が発生する可能性があります。
echo $((6**6**6)) 0 // 6^46656 overflows to 0
echo $((6**6**6**6)) 1 // 6^(6^46656) = 6^0 = 1
echo $((6**6**6**6**6)) 6 // 6^(6(6^46656)) = 6^(6^0) = 6^1
echo $((6**6**6**6**6**6)) 46656 // 6^(6^(6^(6^46656))) = 6^6
echo $((6**6**6**6**6**6**6)) 0 // = 6^6^6^1 = 0
...
を使用sh -c 'command'
しても何も変化しないので、これは正常で準拠した出力であると想定する必要があります。算術の範囲と制限、および式の評価におけるシェルでの意味が基本的かつ具体的に理解できたと思うので、Linuxの他のソフトウェアが使用するデータ型をすぐに確認できると思いました。bash
このコマンドの入力を補完する必要があるいくつかのソースを使用しました。
{ shopt -s globstar; for i in /path/to/source_bash-4.2/include/**/*.h /usr/include/**/*.h; do grep -HE '\b(([UL])|(UL)|())LONG|\bFLOAT|\bDOUBLE|\bINT' $i; done; } | grep -iE 'bash.*max'
bash-4.2/include/typemax.h:# define LLONG_MAX TYPE_MAXIMUM(long long int)
bash-4.2/include/typemax.h:# define ULLONG_MAX TYPE_MAXIMUM(unsigned long long int)
bash-4.2/include/typemax.h:# define INT_MAX TYPE_MAXIMUM(int)
if
ステートメントの出力が増え、コマンドも同様に検索できるようになりawk
ました。使用した正規表現では、bc
and などの任意の精密ツールについて何も検出されませんdc
。
ご質問
awk
算術評価がオーバーフローしたときに警告しない(2 ^ 1024を評価するときのように)理由は何ですか?なぜ2との間に負の整数である63と2 64 -1、彼は何かを評価するのエンドユーザーに公開しますか?- UNIXのいくつかのフレーバーが対話的にULONG_MAXを変更できることをどこかで読んだことがありますか?これを聞いた人はいますか?
- で符号なし整数の最大値を誰かが任意に変更して
limits.h
から再コンパイルしたbash
場合、何が起こると予想できますか?
注意
1.非常に単純な経験的なものであるため、見たものをより明確に説明したかった。私が気づいたのはそれです:
- (a)<2 ^ 63-1を与える評価はすべて正しい
- (b)=> 2 ^ 63から2 ^ 64までの評価では、負の整数が得られます。
- その整数の範囲はx〜yです。x = -9223372036854775808およびy = 0。
これを考慮すると、(b)のような評価は、2 ^ 63-1とx..y内の何かで表すことができます。たとえば、(2 ^ 63-1)+100 002(ただし(a)よりも小さい任意の数になる可能性があります)を評価するように文字どおりに要求された場合、-9223372036854675807が得られます。私は明白だと思いますが、これは次の2つの式が意味することも意味します。
- (2 ^ 63-1)+ 100 002 AND;
- (2 ^ 63-1)+(LLONG_MAX-{シェルが((2 ^ 63-1)+ 100 002)に与えるもの、つまり-9223372036854675807})正の値を使用して、
- (2 ^ 63-1)+(9223372036854775807-9223372036854675807 = 100 000)
- = 9223372036854775807 + 100 000
確かに非常に近いです。2番目の式は(2 ^ 63-1)+ 100 002以外の "2"です。つまり、評価対象です。これは、あなたが2 ^ 64からどれだけ離れているかを示す負の整数を取得するという意味です。つまり、これらの負の整数と制限についての知識があれば、bashシェルのx..yの範囲内で評価を完了することはできませんが、他の場所ではできます-その意味で、データは2 ^ 64まで使用できます(追加できます)紙に書いたり、bcで使用したりします。それを超えると、動作は6 ^ 6 ^ 6の動作に似ています。以下のQで説明されているように制限に達しているためです...
bc
、たとえばを使用します$num=$(echo 6^6^6 | bc)
。残念ながら、bc
改行を入れているため、num=$(echo $num | sed 's/\\\s//g')
後で行う必要があります。パイプで実行すると、実際には機能しますが、sedで扱いにくい改行文字がありますnum=$(echo 6^6^3 | bc | perl -pne 's/\\\s//g')
。どちらの場合でも、使用できる整数が得られnum2=$(echo "$num * 2" | bc)
ます。