Für die arithmetischen Auswertungsfunktionen der bash
Shell sind Grenzen festgelegt . Das Handbuch ist kurz und bündig zu diesem Aspekt der Shell-Arithmetik, lautet jedoch :
Die Auswertung erfolgt in Ganzzahlen mit fester Breite ohne Überprüfung auf Überlauf, obwohl die Division durch 0 abgefangen und als Fehler gekennzeichnet wird. Die Operatoren und ihre Priorität, Assoziativität und Werte sind dieselben wie in der C-Sprache.
Auf welche Ganzzahl mit fester Breite sich dies bezieht, hängt wirklich davon ab, welcher Datentyp verwendet wird (und die Einzelheiten dazu liegen darüber hinaus), aber der Grenzwert wird folgendermaßen ausgedrückt /usr/include/limits.h
:
# if __WORDSIZE == 64
# define ULONG_MAX 18446744073709551615UL
# ifdef __USE_ISOC99
# define LLONG_MAX 9223372036854775807LL
# define ULLONG_MAX 18446744073709551615ULL
Und sobald Sie das wissen, können Sie diesen Sachverhalt folgendermaßen bestätigen:
# getconf -a | grep 'long'
LONG_BIT 64
ULONG_MAX 18446744073709551615
Dies ist eine 64-Bit-Ganzzahl, die im Kontext der arithmetischen Auswertung direkt in die Shell übersetzt wird:
# 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
So zwischen 2 63 und 2 64 -1, erhalten Sie negative ganze Zahlen Sie zeigen , wie weit weg von ULONG_MAX Sie sind 1 . Wenn die Auswertung erreicht diese Grenze und überläuft, durch was auch immer , um das heißt, Sie keine Warnung erhalten und dass ein Teil der Auswertung auf 0 zurückgesetzt, die mit so etwas wie etwas ungewöhnlichen Verhalten ergeben können rechtsassoziativ Potenzierung zum Beispiel:
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
...
Die Verwendung sh -c 'command'
ändert nichts, daher muss ich davon ausgehen, dass dies eine normale und konforme Ausgabe ist. Jetzt, da ich denke, dass ich ein grundlegendes, aber konkretes Verständnis des arithmetischen Bereichs und der Grenze habe und was dies in der Shell für die Auswertung von Ausdrücken bedeutet, dachte ich, ich könnte schnell einen Blick darauf werfen, welche Datentypen die andere Software unter Linux verwendet. Ich habe einige bash
Quellen verwendet, die ich zur Eingabe dieses Befehls ergänzen musste:
{ 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)
Es gibt mehr Leistung mit den if
Aussagen und ich kann wie für einen Befehl suchen mit awk
dem regulären Ausdruck I verfängt nichts über willkürliches Präzisionswerkzeug Ich habe wie verwendet zu etc. Ich Mitteilung bc
und dc
.
Fragen
- Was ist der Grund, warum Sie nicht gewarnt werden (wie
awk
bei der Bewertung von 2 ^ 1024), wenn Ihre arithmetische Bewertung überläuft? Warum sind die negativen Ganzzahlen zwischen 2 63 und 2 64 -1 dem Endbenutzer ausgesetzt, wenn er etwas bewertet? - Ich habe irgendwo gelesen, dass eine UNIX-Variante ULONG_MAX interaktiv ändern kann. Hat jemand davon gehört?
- Wenn jemand den Wert des vorzeichenlosen Ganzzahlmaximums willkürlich ändert
limits.h
und dann neu kompiliertbash
, was können wir dann erwarten?
Hinweis
1. Ich wollte klarer veranschaulichen, was ich gesehen habe, da es sich um sehr einfache empirische Dinge handelt. Was mir aufgefallen ist, ist Folgendes:
- (a) Jede Bewertung, die <2 ^ 63-1 ergibt, ist korrekt
- (b) Jede Bewertung, die => 2 ^ 63 bis 2 ^ 64 ergibt, ergibt eine negative ganze Zahl:
- Der Bereich dieser Ganzzahl liegt zwischen x und y. x = -9223372036854775808 und y = 0.
In Anbetracht dessen kann eine Bewertung, die wie (b) ist, als 2 ^ 63-1 plus etwas innerhalb von x..y ausgedrückt werden. Wenn wir zum Beispiel buchstäblich gebeten werden, (2 ^ 63-1) +100 002 zu bewerten (aber eine beliebige Zahl kleiner als in (a) sein kann), erhalten wir -9223372036854675807. Ich sage nur das Offensichtliche, aber das bedeutet auch, dass die beiden folgenden Ausdrücke:
- (2 ^ 63-1) + 100 002 AND;
- (2 ^ 63-1) + (LLONG_MAX - {was die Shell uns für ((2 ^ 63-1) + 100 002) gibt, das ist -9223372036854675807}) gut, unter Verwendung positiver Werte, die wir haben;
- (2 ^ 63-1) + (9223372036854775807 - 9223372036854675807 = 100 000)
- = 9223372036854775807 + 100 000
sind in der Tat sehr nah. Der zweite Ausdruck ist "2", abgesehen von (2 ^ 63-1) + 100 002, dh was wir bewerten. Das meine ich damit, dass Sie negative ganze Zahlen erhalten, die Ihnen zeigen, wie weit Sie von 2 ^ 64 entfernt sind. Ich meine, mit diesen negativen ganzen Zahlen und der Kenntnis der Grenzen können Sie die Auswertung nicht innerhalb des x..y-Bereichs in der Bash-Shell beenden, aber Sie können anderswo - die Daten können in diesem Sinne bis zu 2 ^ 64 verwendet werden (könnte ich hinzufügen) es auf Papier oder verwenden Sie es in bc). Darüber hinaus ist das Verhalten jedoch dem von 6 ^ 6 ^ 6 ähnlich, da die Grenze erreicht wird, wie nachstehend in Q ...
bc
z $num=$(echo 6^6^6 | bc)
. bc
Setzt leider Zeilenumbrüche ein, so dass Sie num=$(echo $num | sed 's/\\\s//g')
danach; Wenn Sie es in einer Pipe tun, gibt es tatsächlich Zeilenumbrüche, die mit sed umständlich sind, obwohl sie num=$(echo 6^6^3 | bc | perl -pne 's/\\\s//g')
funktionieren. In beiden Fällen haben Sie jetzt eine Ganzzahl, die verwendet werden kann, z num2=$(echo "$num * 2" | bc)
.
bc
durch Einstellen deaktivieren können BC_LINE_LENGTH=0
.