int Overflow / Underflow
-
Hi,
ich hab gehört, dass signed int Overflow / Underflow in C UB ist und im gleichen Atemzug, dass das auch für C++ gölte. Ist das wahr? Ich dacht immer, es sei implementation-defined. Und auf Plattformen die das 2er-Komplement implementieren ist dann auch definiert das max+1=min und min-1=max.
-
Ist undefined.
Der tiefere Grund dafür ist, dass dies recht mächtige Optimierungen erlaubt. Denn oft könnte man genaue Aussagen über das effektive Verhalten eines Programmabschnitts machen, die jedoch mit einem "außer man hat einen Integer Overflow" relativiert werden müssten. Wenn der Optimierer jedoch annehmen darf, dass dieser Fall nie auftritt, dann kann er für das normale Verhalten optimieren, ohne die Sonderfälle zu beachten. Und falls doch ein Overflow auftritt, darf dann Unsinn heraus kommen, weil es undefiniertes Verhalten ist.
Ein einfaches Beispiel. Die typische Schleife:
for(int i = untere_grenze; i < obere_grenze; ++i)...
Man könnte eigentlich sagen, dass diese Schleife genau
obere_grenze - untere_grenze
Durchläufe haben wird. Außer man hat einen Integer Overflow. Der Compiler darf nun aber trotzdem optimierten Code erzeugen, der genauobere_grenze - untere_grenze
mal läuft und wenn dochuntere_grenze > obere_grenze
ist, dann passiert eben irgendwelcher Unsinn, aber das ist dann eben undefiniert.PS: Aus diesem Grund sind Schleifen mit int-Zählvariablen in Mikrobenchmarks oft ein kleines bisschen schneller als mit unsigned int. Denn da Überlauf bei unsigned int genau definiert ist, können obige Optimierungen nicht gemacht werden.
-
Ah, das macht Sinn. Danke!
-
SeppJ schrieb:
PS: Aus diesem Grund sind Schleifen mit int-Zählvariablen in Mikrobenchmarks oft ein kleines bisschen schneller als mit unsigned int. Denn da Überlauf bei unsigned int genau definiert ist, können obige Optimierungen nicht gemacht werden.
Nette Anekdote hier, bei der es genau darum geht (für die, die es noch nicht kennen):
https://www.youtube.com/watch?v=yG1OZ69H_-o&t=39m32s
Hier wird im Code von BZip ein
unsigned
-Typ für eine Index-Variable in ein Array verwendet. Intuitiv hätte ich auch gedacht,
das sei für einen Index keine allzu schlechte Wahl, aber wie man sieht, macht es durchaus Sinn einensigned
-Typen zu
verwenden, wenn man weiss, dass man das wohldefinierte Überlaufverhalten vonunsigned
nicht benötigt: Man lässt
dem Compiler mehr Spielraum für Optimierungen.Der Vortrag ist generell recht interessant und ein Plädoyer für UB.
-
Finnegan schrieb:
SeppJ schrieb:
PS: Aus diesem Grund sind Schleifen mit int-Zählvariablen in Mikrobenchmarks oft ein kleines bisschen schneller als mit unsigned int. Denn da Überlauf bei unsigned int genau definiert ist, können obige Optimierungen nicht gemacht werden.
Nette Anekdote hier, bei der es genau darum geht (für die, die es noch nicht kennen):
https://www.youtube.com/watch?v=yG1OZ69H_-o&t=39m32s
Hier wird im Code von BZip ein
unsigned
-Typ für eine Index-Variable in ein Array verwendet. Intuitiv hätte ich auch gedacht,
das sei für einen Index keine allzu schlechte Wahl, aber wie man sieht, macht es durchaus Sinn einensigned
-Typen zu
verwenden, wenn man weiss, dass man das wohldefinierte Überlaufverhalten vonunsigned
nicht benötigt: Man lässt
dem Compiler mehr Spielraum für Optimierungen.Der Vortrag ist generell recht interessant und ein Plädoyer für UB.
Wobei das Problem hier nur entsteht, weil künstlich explizit auf 32 bit heruntergerechnet wird; zwischen ptrdiff_t und size_t sollte kein Unterschied bestehen (außer ggf. mit x32-ABI, wobei der Compiler hier ggf schummeln und gleich explizit 32bit-Adressierung verwenden könnte, dass kostet dann nur ein extra Byte (REX) und nicht gleich 3 zusätzliche Instruktionen). Das bedarf dann auch keiner besonderen Erläuterung.