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.


  • Mod

    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 genau obere_grenze - untere_grenze mal läuft und wenn doch untere_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 einen signed -Typen zu
    verwenden, wenn man weiss, dass man das wohldefinierte Überlaufverhalten von unsigned 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.


  • Mod

    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 einen signed -Typen zu
    verwenden, wenn man weiss, dass man das wohldefinierte Überlaufverhalten von unsigned 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.


Log in to reply