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 Sienum=$(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 sienum=$(echo 6^6^3 | bc | perl -pne 's/\\\s//g')
funktionieren. In beiden Fällen haben Sie jetzt eine Ganzzahl, die verwendet werden kann, znum2=$(echo "$num * 2" | bc)
.bc
durch Einstellen deaktivieren könnenBC_LINE_LENGTH=0
.Antworten:
Nein . Wie kommst du darauf? Nach Ihrem eigenen Beispiel ist das Maximum:
Wenn "Überlauf" bedeutet "Sie erhalten negative Ganzzahlen, die Ihnen zeigen, wie weit Sie von ULONG_MAX entfernt sind", sollten wir dann nicht -1 erhalten, wenn wir eine hinzufügen? Aber stattdessen:
Vielleicht meinen Sie, dass dies eine Zahl ist, die Sie hinzufügen können
$max
, um einen negativen Unterschied zu erhalten, da:Dies gilt jedoch nicht weiter:
Dies liegt daran, dass das System das Zweierkomplement verwendet , um vorzeichenbehaftete Ganzzahlen zu implementieren. 1 Der Wert, der sich aus einem Überlauf ergibt, ist KEIN Versuch, Ihnen eine Differenz, eine negative Differenz usw. zu liefern. Er ist buchstäblich das Ergebnis des Abschneidens eines Werts auf eine begrenzte Anzahl von Bits und der anschließenden Interpretation als Ganzzahl mit Zweierkomplement . Der Grund
$(($max + 1 + $max))
für -1 ist beispielsweise, dass der höchste Wert im Zweierkomplement alle gesetzten Bits außer dem höchsten Bit (was negativ anzeigt) sind. Wenn Sie diese addieren, müssen Sie im Grunde alle Bits nach links tragen, sodass Sie am Ende (wenn die Größe 16 Bit und nicht 64 Bit beträgt) Folgendes haben:Das High-Bit (Vorzeichen) wird jetzt gesetzt, da es in der Addition übertragen wurde. Wenn Sie noch eine (00000000 00000001) hinzufügen, sind alle Bits gesetzt , was im Zweierkomplement -1 ist.
Ich denke, das beantwortet teilweise die zweite Hälfte Ihrer ersten Frage - "Warum sind die negativen ganzen Zahlen ... dem Endbenutzer ausgesetzt?". Erstens, weil dies der richtige Wert gemäß den Regeln der 64-Bit-Zweierkomplementzahlen ist. Dies ist die konventionelle Praxis der meisten (anderen) Allzweck-Programmiersprachen auf hoher Ebene (ich kann mir keine vorstellen, die dies nicht tut), daher
bash
wird die Konvention eingehalten. Welches ist auch die Antwort auf den ersten Teil der ersten Frage - "Was ist die Begründung?": Dies ist die Norm in der Spezifikation von Programmiersprachen.WRT die 2. Frage, ich habe noch nichts von Systemen gehört, die ULONG_MAX interaktiv verändern.
Es würde keinen Unterschied machen, wie die Arithmetik herauskommt, da dies kein willkürlicher Wert ist, der zum Konfigurieren des Systems verwendet wird - es ist ein Komfortwert, der eine unveränderliche Konstante speichert, die die Hardware widerspiegelt. In Analogie könnten Sie c auf 55 Meilen pro Stunde neu definieren , aber die Lichtgeschwindigkeit beträgt immer noch 186.000 Meilen pro Sekunde. c ist keine Zahl, die zur Konfiguration des Universums verwendet wird - es ist ein Abzug über die Natur des Universums.
ULONG_MAX ist genau das gleiche. Sie wird basierend auf der Art der N-Bit-Zahlen abgeleitet / berechnet. Eine Änderung
limits.h
wäre eine sehr schlechte Idee, wenn diese Konstante irgendwo verwendet wird, vorausgesetzt, sie soll die Realität des Systems darstellen .Und Sie können die von Ihrer Hardware auferlegte Realität nicht ändern.
1. Ich denke nicht, dass dies (das Mittel der Ganzzahldarstellung) tatsächlich durch garantiert wird
bash
, da es von der zugrunde liegenden C-Bibliothek abhängt und Standard C dies nicht garantiert. Dies wird jedoch auf den meisten normalen modernen Computern verwendet.quelle
$max
wie beschrieben im Bereich> 2 * befinden . Meine Punkte sind 1) das ist nicht der Zweck, 2) stellen Sie sicher, dass Sie verstehen, wenn Sie das tun möchten, 3) es ist nicht sehr nützlich wegen der sehr eingeschränkten Anwendbarkeit, 4) gemäß der Fußnote ist es nicht wirklich garantiert, dass das System dies tut Verwenden Sie das Zweierkomplement. Kurz gesagt, der Versuch, dies im Programmcode auszunutzen, wäre eine sehr schlechte Praxis. Es gibt "große Anzahl" Bibliotheken / Module (für Shells unter POSIXbc
) - verwenden Sie diese, wenn nötig.