私のスクリプトがそれ自体の最大数を決定できるようにするにはどうすればよいですか?
私の環境変数を見て、有望に見える次の2つを見つけました。
~# declare -p BASH_VERSINFO HOSTTYPE
declare -ar BASH_VERSINFO=([0]="5" [1]="0" [2]="11" [3]="1" [4]="release" [5]="x86_64-slackware-linux-gnu")
declare -- HOSTTYPE="x86_64"
...しかし、Bash算術の最大数が何であるかを結論付けるためにこれを解析できると本当に信頼できますか?プログラムでより良い方法が必要です。どんな提案がありますか?
答え1
Bash算術は符号付き数字を使用します。
したがって、迅速な答えは次のようになります。
((MAX=(1<<63)-1))
しかし、スクリプトが実行されているシステムのビットに対して不可知論的であることを望んでいるので、続行します。
無差別代入は、負にオーバーフローする点に達するまでループに1を追加し続けます。しかし、これは数年かかるかもしれません! :-) より高速でエレガントな方法は、単純なビットシフトを使用することです。
符号ビットを探しましょう。つまり、1
最上位ビットから数字を探し、数に関係なく他のすべてのビットでゼロを探します。この数字を取得したら、1
数字を減算すると最大の符号付き数字が得られます。
# MIN -- the smallest signed number 0x8000...00 (it equals MAX+1)
# MAX -- the largest signed number 0x7Fff...FF <-- what we are looking for
MIN=1; until (( (MIN<<=1) < 0 )) ;do :;done
((MAX=MIN-1))
echo $MAX
Result:
9223372036854775807
またはループのない単一行です。数値の16進表現を変数に入れ、それを組み込み関数に渡すときに変数拡張を介して符号ビットをマスクしますprintf
。
printf -v MAX %x -1 && printf -v MAX %d 0x${MAX/f/7}
echo $MAX
Result:
9223372036854775807
私と異なる桁数を持つコンピュータでは、結果は異なる数値になります。
説明のために、私の場合は次のようになります。
printf "MAX %X %d\nMIN %X %d\n" $MAX $MAX $MIN $MIN
MAX 7FFFFFFFFFFFFFFF 9223372036854775807
MIN 8000000000000000 -9223372036854775808
MINに関するいくつかの注意:MINのみを使用するように制限したい場合があります((MIN=-MAX))
。そうしないと、一部の算術演算に問題が発生することがあります。
((MIN=-MAX)) ; printf "MIN %X %d\n" $MIN $MIN
MIN 8000000000000001 -9223372036854775807
答え2
2段階のテキスト操作で最大量を取得できます。
TL;博士:あなたがしなければならないbash
ことは次のとおりです。
printf -v ff %x -1
printf -v max %d "0x${ff/#?/7}"
そして、次の追加操作で最小値を得ることができます。
# this uses the shell's own arithmetic engine
min="$((max+1))"
OPのコメントに答えるには、組み込みコマンドとしてbash
実装されており、デフォルトではprintf
組み込みコマンドを好むので、通常printf
はスタンドアロン実行可能ファイルとして使用される外部コマンドを呼び出そうとしません$PATH
。
また、上記のように独自bash
の組み込み関数を使用すると、追加のprintf
プロセスは作成されません。代わりに、変数が作成され、$ff
便利$max
なオプションで指定されているシェルの現在の実行環境で実行されます-v
。
上記と同じですが、POSIX互換の構文は次のとおりです。
ff="$(printf %x -1)"
max="$(printf %d "0x7${ff#?}")"
# to obtain the minimum number in shells that only
# support integer numbers you can just do like
# said for `bash`
min="$((max+1))"
# else for shells defaulting to floating-point numbers
# (such as ksh93) you might instead do one text-manipulation
# operation on top of the arithmetic addition
min="$(printf -- -%u "$((max+1))")"
# of course such result would only apply to the shell's
# integer capacities, not to its floating-point capacities
上記のPOSIX互換構文を使用していることに注意してください。可能特定のシェルがprintf
同様の単純なコマンドを最適化するかどうかに応じて、各コマンド置換の (一時的な) プロセスを作成します。ただし、シェルがこれらのプロセスごとにプロセスを作成しても、printf
単にそれ自体が分岐して実行されるため、他の任意のシェルの演算機能ではなく、独自の算術機能に依然として依存します。
これは、シェルが次のprintf
ように実装されていると仮定します。組み込み、bash
他の多くの殻と同様に。組み込みで実装されていないシェルは、使用可能なprintf
外部コマンド(存在する場合)を使用します。これは、外部コマンド、オペレーティングシステムのCライブラリ、およびCPU自体の機能に関して有効な結果を生成しますが、必ずしも次のものと一致しない可能性があります。printf
$PATH
printf
シェル自身mksh
64ビットシステムでも32ビット算術を使用するなどの算術機能。組み込みコマンドでmksh
実装されていない場合、外部コマンドが依然として優先される可能性があり、組み込みコマンドにコンパイルされていない場合は組み込みコマンドとして実装されない可能性があることに注意する価値があります。printf
ash
busybox
printf
答え3
私は64ビットコンピュータに座っています。しかし、スクリプトに自分自身を決定させるにはどうすればよいですか?
これはbash算術の最大数が何であるかとは異なる質問です。これを使用してコンピュータのビット数を決定することはできず、コンピュータのビット数はBash整数のサイズを決定しません。
Bash番号は、32ビットシステムでも常に64ビットです。 (またはISO Cが必要とする少なくとも64ビットよりも広いエキゾチックなシステムでは、より広いかもしれません。long long
)念頭に置いてください。
min = -max
-max - 1
long long
unsigned long long
好奇心が強いので、Bash 3.2.39を実行している古い32ビットDebianシステムでこれをテストしました。 LL3の方法は、printf %x -1
16f
秒(つまり8バイト、64ビット)を印刷することを示しています。
INT64_MAXをINT64_MINに増やしてbash数学をテストしました。
$ uname -a
Linux <non-updated kernel version hidden to protect the guilty> i686 GNU/Linux
$ echo $((9223372036854775807 + 1))
-9223372036854775808
Bashのソースコードは、32ビットシステムで32ビットと同じ型ではないint64_t
or long long
(またはuint64_t
その動作を定義するために構築されていない限り、ラップするときにCの未定義の動作を避けたい)を使用することは明らかです。gcc -fwrapv
long
32ビットシステムで64ビットを追加すると、コンパイラはキャリーフラグを持つシステムでadd
+(キャリーを含む追加)またはキャリーフラグを持たないシステムで3番目の命令などの2つの命令を使用します。adc
したがって、言語が64ビットタイプを提供するのは非常に正常であり、1つのタイプしかない高級言語の場合は、良いワイドタイプを使用することは非常に正常です。