bitlvl
DE

Integer-Überlauf- & Arithmetik-Rechner

Tippen Sie einen Ausdruck, wählen Sie einen Typ und eine Sprache und sehen Sie genau, was die Maschine tut: welche Operanden promotet oder konvertiert werden, wo das Ergebnis nicht mehr passt, ob das Wraparound, ein Panic oder undefiniertes Verhalten ist, und die Bits davor und danach.

Die Ergebnisse und Meldungen dieses Tools werden auf Englisch angezeigt.

Warum Ganzzahlen überraschen

Eine Maschinen-Ganzzahl hat eine feste Anzahl Bits, jede Operation kann also einen Wert liefern, der nicht hineinpasst. Was dann passiert, entscheidet nicht die Hardware (die einfach umläuft), sondern die Sprache:

SpracheÜberlauf mit VorzeichenÜberlauf ohne VorzeichenShift um ≥ BreiteDivision durch null
C / C++undefiniertes Verhalten: Der Optimierer nimmt an, dass es nie passiertläuft modulo 2ⁿ um (definiert)undefiniertundefiniert (SIGFPE auf x86)
RustPanic im Debug-Modus, Wrap im Release; checked_/wrapping_/saturating_/overflowing_ sind explizitwie mit VorzeichenPanic im Debug-ModusPanic
Goläuft um (definiert)läuft um0 / −1, kein PanicPanic
Javaläuft um (definiert); Math.addExact wirftkeine vorzeichenlosen Typen (char ist 16-Bit unsigned)Zähler auf 5/6 Bit maskiertArithmeticException
JavaScriptZahlen sind Doubles: exakt bis 2⁵³, danach Rundung; Bit-Operatoren arbeiten auf int32Zähler auf 5 Bit maskiertInfinity / NaN

Die C-Regeln, die zubeißen

  • Integer-Promotion. Alles, was kleiner als int ist, wird vor der Arithmetik zu int, also ist uint8_t a = 200, b = 100; a + b gleich 300, und erst die Zuweisung zurück in ein uint8_t schneidet auf 44 ab. ~x auf einem uint8_t ergibt einen negativen int.
  • Übliche arithmetische Konvertierungen. Trifft signed auf unsigned gleicher Breite, wird die signed-Seite unsigned: -1 > 0u ist wahr, if (len - 1 < 0) ist nie wahr, wenn len ein size_t ist.
  • Signed-Überlauf ist UB. if (x + 1 < x) als Überlaufprüfung wird von GCC und Clang mit Optimierung gelöscht, weil sie annehmen dürfen, dass x + 1 nie überläuft. Prüfen Sie vor der Operation (x > INT_MAX - y) oder verwenden Sie __builtin_add_overflow.
  • Shifts. Um die Breite oder mehr zu schieben oder einen negativen Wert links zu schieben ist undefiniert; x86 maskiert den Zähler, ARM liefert null, der Compiler tut, was sein Modell sagt.
  • Division. Schneidet Richtung null ab, also -7 / 2 == -3 und -7 % 2 == -1; INT_MIN / -1 läuft über und löst auf x86 tatsächlich einen Trap aus.

Die Ausgabe lesen

Das Ergebnis wird in dem Typ gezeigt, in dem es landet, mit seinem Bitmuster und denselben Bits in der entgegengesetzten Vorzeichenlesart, also dem, was Sie sehen, wenn ein Wert eine (int)-/(unsigned)-Grenze überschreitet oder durch einen anderen Typ gespeichert und zurückgelesen wird. Die Schrittliste ist die Auswertungsreihenfolge mit dem Typ in jedem Schritt; die Hinweise sind die Stellen, an denen die Sprache etwas anderes als einfache Arithmetik getan hat.

Fragen

Warum weicht das C-Ergebnis von dem ab, was mein Programm ausgegeben hat?

Für undefiniertes Verhalten gibt es keine einzige richtige Antwort: Diese Seite zeigt den Wraparound-Wert, den unoptimierter Code liefert, und markiert den Schritt als UB. Mit -O2 hat der Compiler vielleicht angenommen, dass der Überlauf nicht vorkommen kann, und anderen Code erzeugt. Kompilieren Sie mit -fsanitize=undefined, um die genaue Stelle zu sehen.

Was macht die Typauswahl?

Sie bestimmt den Typ von Literalen ohne Suffix oder Cast, der in C standardmäßig int ist. Wählen Sie u8 und tippen Sie 200 + 100, um Rust panicken zu sehen, oder C auf int promoten und gelingen.

Wie schreibe ich einen Cast?

C-Stil: (u8) expr, (uint32_t) expr, (int) expr. Rust-Stil: expr as u8. Beides funktioniert in jedem Sprachmodus. INT_MAX, INT_MIN, UINT_MAX, LONG_MAX, SIZE_MAX stehen als Namen zur Verfügung.

Kann es 128 Bit?

Ja: i128 und u128 werden exakt ausgewertet (alle Arithmetik läuft mit beliebiger Genauigkeit und wird dann in den Typ eingepasst).

Und Gleitkommazahlen?

Nicht hier; dafür gibt es den IEEE-754-Float-Visualizer auf dieser Seite. Dieses Tool behandelt nur, was mit ganzen Zahlen in Typen fester Breite passiert.