GNU/Linux >> Linux の 問題 >  >> Linux

バッシュシェルが算術オーバーフローなどを警告しない理由は?

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と2-1の間では、ULONG_MAXからどれだけ離れているかを示す負の整数が得られます。評価がその制限に達してオーバーフローすると、どのような順序でも警告は表示されず、評価のその部分は0にリセットされます。これにより、 right-associativeなどの異常な動作が発生する可能性があります。 たとえば、べき乗:

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などの任意精度ツールについて何もキャッチしないことに気付きました。 およびdc

質問

  1. 警告しない理由は何ですか(awkなど) 算術評価がオーバーフローした場合、2 ^ 1024)を評価しますか?エンドユーザーが何かを評価するときに、2と2-1の間の負の整数が公開されるのはなぜですか?
  2. UNIXの一部のフレーバーがULONG_MAXをインタラクティブに変更できることをどこかで読んだことがありますか?誰かがこれを聞いたことがありますか?
  3. 誰かがlimits.hの符号なし整数の最大値を任意に変更した場合 、次にbashを再コンパイルします 、何が起こると期待できますか?

関連:bashまたは他の言語/フレームワークで整数と浮動小数点の計算を行う方法は?

承認された回答:

したがって、2^63と2^64-1の間では、ULONG_MAXからどれだけ離れているかを示す負の整数が得られます。

いいえ。どうやってそれを理解しますか? あなた自身の例では、最大値は次のとおりです。

> max=$((2**63 - 1)); echo $max
9223372036854775807

「オーバーフロー」が「ULONG_MAXからどれだけ離れているかを示す負の整数を取得する」ことを意味する場合、それに1を追加すると、-1を取得するべきではありませんか?しかし代わりに:

> echo $(($max + 1))
-9223372036854775808

おそらく、これは$maxに追加できる数値であることを意味します 負の差を得るには:

> echo $(($max + 1 + $max))
-1

しかし、これは実際には当てはまりません:

> echo $(($max + 2 + $max))
0

これは、システムが2の補数を使用して符号付き整数を実装するためです。 オーバーフローの結果として生じる値は、差や負の差などを提供する試みではありません。 これは、文字通り、値を限られたビット数に切り捨ててから、2の補数の符号付き整数として解釈した結果です。たとえば、理由$(($max + 1 + $max)) -1として出力されるのは、2の補数の最大値が、を除くに設定されたすべてのビットであるためです。 最上位ビット(負を示す)。これらを足し合わせるということは、基本的にすべてのビットを左側に運ぶことを意味するので、最終的には次のようになります(サイズが64ではなく16ビットの場合):

11111111 11111110

加算で繰り越されたため、上位(符号)ビットが設定されるようになりました。これにもう1つ(00000000 00000001)を追加すると、すべてのビットが設定されます 、2の補数で-1です。

これは、最初の質問の後半、「なぜ負の整数が…エンドユーザーに公開されるのか」に部分的に答えると思います。まず、これは64ビット2の補数の規則に従った正しい値だからです。これは、ほとんどの(他の)汎用高水準プログラミング言語の従来の慣習です(これを行わない言語は考えられません)。したがって、bash 慣習を守っています。これは、最初の質問の最初の部分である「理論的根拠は何ですか?」に対する答えでもあります。これは、プログラミング言語の仕様の標準です。

WRTの2番目の質問ですが、ULONG_MAXをインタラクティブに変更するシステムについては聞いたことがありません。

誰かがlimits.hの符号なし整数の最大値を任意に変更してからbashを再コンパイルした場合、何が起こると予想できますか?

これはシステムの構成に使用される任意の値ではないため、算術演算の結果に違いはありません。ハードウェアを反映する不変の定数を格納する便利な値です。類推すると、 cを再定義できます。 時速55マイルになりますが、光速はそれでも毎秒186,000マイルになります。 c は宇宙を構成するために使用される数ではありません—それは宇宙の性質についての控除です。

関連:Python –そのようなファイルやディレクトリはありませんが、表示されます!?

ULONG_MAXはまったく同じです。これは、Nビット数の性質に基づいて推定/計算されます。 limits.hで変更する その定数がシステムの現実を表すことになっていると仮定してどこかで使用される場合、それは非常に悪い考えです

また、ハードウェアによって課せられる現実を変えることはできません。


Linux
  1. スクリプト ファイルの先頭に #!/bin/bash を配置する必要があるのはなぜですか?

  2. Bash シェル スクリプト - フラグをチェックしてその値を取得する

  3. スクリプト内の bash シェル スクリプトおよび関数の変数スコープ

  1. シェルドットファイルでできること

  2. Bashシェルのカスタマイズ

  3. Bash For Loopsの「do」キーワードの目的は?

  1. `$ _`の意味を理解しますか?

  2. ヒアドキュメントの親シェルがダッシュのサブコマンドでは機能しないのに、Bashは機能するのはなぜですか?

  3. Bashの正規表現が変数であり、直接ではない場合にのみ機能するのはなぜですか?