it-swarm.com.de

Wie erkenne ich einen vorzeichenlosen Integer-Multiplikationsüberlauf?

Ich habe ein Programm in C++ geschrieben, um alle Lösungen von a zu finden b  = c , wobei a , b und c Verwenden Sie alle Ziffern 0-9 genau einmal. Das Programm durchlief die Werte von a und b und führte jedes Mal eine Ziffernzählroutine für ) durch + a , b und ab um zu prüfen, ob die Ziffernbedingung erfüllt ist.

Es können jedoch falsche Lösungen generiert werden, wenn a b  Überläuft die Ganzzahlgrenze. Ich habe dies mit folgendem Code überprüft:

unsigned long b, c, c_test;
...
c_test=c*b;         // Possible overflow
if (c_test/b != c) {/* There has been an overflow*/}
else c=c_test;      // No overflow

Gibt es eine bessere Möglichkeit zum Testen auf Überlauf? Ich weiß, dass einige Chips ein internes Flag haben, das gesetzt wird, wenn ein Überlauf auftritt, aber ich habe noch nie gesehen, dass über C oder C++ darauf zugegriffen wird.


Beachten Sie, dass unterzeichnet int Überlauf undefiniertes Verhalten in C und C++ ist, und damit Sie müssen es erkennen, ohne es tatsächlich zu verursachen. Informationen zum vorzeichenbehafteten int-Überlauf vor dem Hinzufügen finden Sie unter Erkennen eines vorzeichenbehafteten Überlaufs in C/C++.

580
Chris Johnson

Ich sehe, dass Sie vorzeichenlose ganze Zahlen verwenden. Per Definition in C (keine Ahnung von C++) läuft die vorzeichenlose Arithmetik nicht über ... also ist Ihr Punkt zumindest für C strittig :)

Bei Ganzzahlen mit Vorzeichen ist nach einem Überlauf ndefiniertes Verhalten aufgetreten, und Ihr Programm kann alles (z. B. Tests nicht eindeutig machen).

#include <limits.h>
int a = <something>;
int x = <something>;
a += x;              /* UB */
if (a < 0) {         /* unreliable test */
  /* ... */
}

Um ein konformes Programm zu erstellen, müssen Sie den Überlauf testen , bevor Sie ihn generieren . Die Methode kann auch mit Ganzzahlen ohne Vorzeichen verwendet werden

// for addition
#include <limits.h>
int a = <something>;
int x = <something>;
if ((x > 0) && (a > INT_MAX - x)) /* `a + x` would overflow */;
if ((x < 0) && (a < INT_MIN - x)) /* `a + x` would underflow */;

// for subtraction
#include <limits.h>
int a = <something>;
int x = <something>;
if ((x < 0) && (a > INT_MAX + x)) /* `a - x` would overflow */;
if ((x > 0) && (a < INT_MIN + x)) /* `a - x` would underflow */;

// for multiplication
#include <limits.h>
int a = <something>;
int x = <something>;
// there may be need to check for -1 for two's complement machines
// if one number is -1 and another is INT_MIN multiplying them we get abs(INT_MIN) which is 1 higher than INT_MAX
if ((a == -1) && (x == INT_MIN)) /* `a * x` can overflow */
if ((x == -1) && (a == INT_MIN)) /* `a * x` (or `a / x`) can overflow */
// general case
if (a > INT_MAX / x) /* `a * x` would overflow */;
if ((a < INT_MIN / x)) /* `a * x` would underflow */;

für die Unterteilung (mit Ausnahme des Sonderfalls INT_MIN und -1) gibt es keine Möglichkeit, INT_MIN oder INT_MAX zu wiederholen.

203
pmg

Es gibt is eine Möglichkeit zu bestimmen, ob eine Operation wahrscheinlich überläuft, indem die Positionen der höchstwertigen Ein-Bits in den Operanden und ein wenig grundlegendes binär-mathematisches Wissen verwendet werden.

Zusätzlich führen zwei beliebige Operanden zu (höchstens) einem Bit mehr als dem höchsten Ein-Bit des größten Operanden. Zum Beispiel:

bool addition_is_safe(uint32_t a, uint32_t b) {
    size_t a_bits=highestOneBitPosition(a), b_bits=highestOneBitPosition(b);
    return (a_bits<32 && b_bits<32);
}

Bei der Multiplikation ergeben zwei beliebige Operanden (höchstens) die Summe der Bits der Operanden. Zum Beispiel:

bool multiplication_is_safe(uint32_t a, uint32_t b) {
    size_t a_bits=highestOneBitPosition(a), b_bits=highestOneBitPosition(b);
    return (a_bits+b_bits<=32);
}

Auf ähnliche Weise können Sie die maximale Größe des Ergebnisses von a nach b schätzen:

bool exponentiation_is_safe(uint32_t a, uint32_t b) {
    size_t a_bits=highestOneBitPosition(a);
    return (a_bits*b<=32);
}

(Ersetzen Sie die Anzahl der Bits natürlich durch Ihre Ziel-Ganzzahl.)

Ich bin mir nicht sicher, wie ich am schnellsten die Position des höchsten Ein-Bit-Werts einer Zahl ermitteln kann. Hier ist eine Brute-Force-Methode:

size_t highestOneBitPosition(uint32_t a) {
    size_t bits=0;
    while (a!=0) {
        ++bits;
        a>>=1;
    };
    return bits;
}

Es ist nicht perfekt, aber das gibt Ihnen eine gute Vorstellung davon, ob zwei Zahlen überlaufen könnten, bevor Sie die Operation ausführen. Ich weiß nicht, ob es aufgrund der Schleife in der Funktion highestOneBitPosition schneller wäre, als das Ergebnis einfach so zu überprüfen, wie Sie es vorgeschlagen haben, aber es könnte sein (insbesondere, wenn Sie vorher wussten, wie viele Bits in den Operanden waren) .

164
Head Geek

Clang 3.4 + und GCC 5 + bieten überprüfte arithmetische Funktionen. Sie bieten eine sehr schnelle Lösung für dieses Problem, insbesondere im Vergleich zu Sicherheitsprüfungen mit Bit-Tests.

Für das Beispiel in der Frage von OP würde es so funktionieren:

unsigned long b, c, c_test;
if (__builtin_umull_overflow(b, c, &c_test))
{
    // returned non-zero: there has been an overflow
}
else
{
    // return zero: there hasn't been an overflow
}

In der Clang-Dokumentation wird nicht angegeben, ob c_test das übergelaufene Ergebnis enthält, wenn ein Überlauf aufgetreten ist. In der GCC-Dokumentation wird dies jedoch angegeben. Angesichts der Tatsache, dass diese beiden gerne __builtin -kompatibel sind, kann man davon ausgehen, dass Clang auch so funktioniert.

Für jede arithmetische Operation, die überlaufen kann (Addition, Subtraktion, Multiplikation), gibt es einen __builtin mit vorzeichenbehafteten und vorzeichenlosen Varianten für int-Größen, long-Größen und long-Größen. Die Syntax für den Namen lautet __builtin_[us](operation)(l?l?)_overflow:

  • u für ohne Vorzeichen oder s für mit Vorzeichen ;
  • operation ist eine von add, sub oder mul;
  • nein l Suffix bedeutet, dass die Operanden ints sind; ein l bedeutet long; zwei l bedeuten long long.

Für eine mit einem Häkchen versehene lange Ganzzahladdition wäre es also __builtin_saddl_overflow. Die vollständige Liste finden Sie auf der Clang-Dokumentationsseite .

GCC 5+ und Clang 3.8+ bieten zusätzlich generische Buildins, die ohne Angabe des Wertetyps funktionieren: __builtin_add_overflow, __builtin_sub_overflow und __builtin_mul_overflow. Diese funktionieren auch bei Typen, die kleiner als int sind.

Die integrierten Funktionen sind niedriger als die für die Plattform am besten geeigneten. Auf x86 überprüfen sie die Übertragungs-, Überlauf- und Signierungsflags.

Visual Studios cl.exe hat keine direkten Entsprechungen. Für vorzeichenlose Additionen und Subtraktionen, einschließlich <intrin.h>, können Sie addcarry_uNN und subborrow_uNN verwenden (wobei NN die Anzahl der Bits ist, wie addcarry_u8 oder subborrow_u64) . Ihre Unterschrift ist etwas stumpf:

unsigned char _addcarry_u32(unsigned char c_in, unsigned int src1, unsigned int src2, unsigned int *sum);
unsigned char _subborrow_u32(unsigned char b_in, unsigned int src1, unsigned int src2, unsigned int *diff);

c_in/b_in ist das Carry/Borrow-Flag bei der Eingabe, der Rückgabewert ist das Carry/Borrow bei der Ausgabe. Es scheint keine Entsprechungen für vorzeichenbehaftete Operationen oder Multiplikationen zu geben.

Ansonsten ist Clang für Windows jetzt produktionsbereit (gut genug für Chrome), sodass dies auch eine Option sein könnte.

136
zneak

Einige Compiler bieten Zugriff auf das Integer-Overflow-Flag in der CPU, das Sie dann testen können, aber das ist nicht Standard.

Sie können auch die Möglichkeit eines Überlaufs testen, bevor Sie die Multiplikation durchführen:

if ( b > ULONG_MAX / a ) // a * b would overflow
51
Robert Gamble

Warnung: GCC kann eine Überlaufprüfung beim Kompilieren mit -O2 optimieren. Die Option -Wall gibt in einigen Fällen eine Warnung aus

if (a + b < a) { /* deal with overflow */ }

aber nicht in diesem Beispiel:

b = abs(a);
if (b < 0) { /* deal with overflow */ }

Die einzige sichere Möglichkeit besteht darin, vor dem Auftreten auf Überlauf zu prüfen, wie im CERT-Dokument beschrieben, und es wäre unglaublich mühsam, dies systematisch zu verwenden.

Kompilieren mit -fwrapv löst das Problem, deaktiviert jedoch einige Optimierungen.

Wir brauchen dringend eine bessere Lösung. Ich denke, der Compiler sollte standardmäßig eine Warnung ausgeben, wenn eine Optimierung vorgenommen wird, bei der kein Überlauf auftritt. Die gegenwärtige Situation ermöglicht es dem Compiler, eine Überlaufprüfung zu optimieren, was meiner Meinung nach inakzeptabel ist.

38
A Fog

clang unterstützt jetzt dynamische Überlaufprüfungen sowohl für Ganzzahlen mit als auch ohne Vorzeichen. Siehe - fsanitize = integer switch. Derzeit ist es nur ein C++ - Compiler mit vollständig unterstützter dynamischer Überlaufprüfung für Debug-Zwecke.

30
ZAB

Ich sehe, dass viele Leute die Frage nach dem Überlauf beantwortet haben, aber ich wollte sein ursprüngliches Problem ansprechen. Er sagte, das Problem sei, eine zu findenb= c so, dass alle Ziffern ohne Wiederholung verwendet werden. Ok, das hat er in diesem Beitrag nicht gefragt, aber ich denke immer noch, dass es notwendig war, die Obergrenze des Problems zu untersuchen und daraus zu schließen, dass er niemals einen Überlauf berechnen oder erkennen müsste (Anmerkung: Ich bin nicht kompetent In Mathe habe ich das Schritt für Schritt gemacht, aber das Endergebnis war so einfach, dass dies eine einfache Formel haben könnte.

Der Hauptpunkt ist, dass die obere Schranke, die das Problem für a, b oder c benötigt, 98.765.432 ist. Wie auch immer, indem Sie das Problem in triviale und nicht triviale Teile aufteilen:

  • x == 1 (alle Permutationen von 9, 8, 7, 6, 5, 4, 3, 2 sind Lösungen)
  • x1 == x (keine Lösung möglich)
  • b == 0 (keine Lösung möglich)
  • 1b == 1 (keine Lösung möglich)
  • einb, a> 1, b> 1 (nicht trivial)

Jetzt müssen wir nur zeigen, dass keine andere Lösung möglich ist und nur die Permutationen gültig sind (und dann ist der Code zum Drucken trivial). Wir gehen zurück zur oberen Grenze. Tatsächlich ist die Obergrenze c ≤ 98,765,432. Dies ist die Obergrenze, da dies die größte Zahl mit 8 Stellen ist (10 Stellen insgesamt minus 1 für jedes a und b). Diese Obergrenze gilt nur für c, da die Grenzen für a und b aufgrund des exponentiellen Wachstums viel niedriger sein müssen, wie wir berechnen können, wobei b von 2 bis zur Obergrenze variiert:

    9938.08^2 == 98765432
    462.241^3 == 98765432
    99.6899^4 == 98765432
    39.7119^5 == 98765432
    21.4998^6 == 98765432
    13.8703^7 == 98765432
    9.98448^8 == 98765432
    7.73196^9 == 98765432
    6.30174^10 == 98765432
    5.33068^11 == 98765432
    4.63679^12 == 98765432
    4.12069^13 == 98765432
    3.72429^14 == 98765432
    3.41172^15 == 98765432
    3.15982^16 == 98765432
    2.95305^17 == 98765432
    2.78064^18 == 98765432
    2.63493^19 == 98765432
    2.51033^20 == 98765432
    2.40268^21 == 98765432
    2.30883^22 == 98765432
    2.22634^23 == 98765432
    2.15332^24 == 98765432
    2.08826^25 == 98765432
    2.02995^26 == 98765432
    1.97741^27 == 98765432

Beachten Sie zum Beispiel die letzte Zeile: Sie besagt, dass 1,97 ^ 27 ~ 98M. Zum Beispiel 1 ^ 27 == 1 und 2 ^ 27 == 134.217.728 und das ist keine Lösung, weil es 9 Stellen hat (2> 1.97, also ist es tatsächlich größer als das, was getestet werden sollte). Wie man sieht, sind die Kombinationen, die zum Testen von a und b zur Verfügung stehen, sehr klein. Für b == 14 müssen wir 2 und 3 versuchen. Für b == 3 beginnen wir bei 2 und enden bei 462. Alle Ergebnisse werden mit weniger als ~ 98 M angegeben.

Testen Sie jetzt einfach alle oben genannten Kombinationen und suchen Sie nach denjenigen, die keine Ziffern wiederholen:

    ['0', '2', '4', '5', '6', '7', '8'] 84^2 = 7056
    ['1', '2', '3', '4', '5', '8', '9'] 59^2 = 3481
    ['0', '1', '2', '3', '4', '5', '8', '9'] 59^2 = 3481 (+leading zero)
    ['1', '2', '3', '5', '8'] 8^3 = 512
    ['0', '1', '2', '3', '5', '8'] 8^3 = 512 (+leading zero)
    ['1', '2', '4', '6'] 4^2 = 16
    ['0', '1', '2', '4', '6'] 4^2 = 16 (+leading zero)
    ['1', '2', '4', '6'] 2^4 = 16
    ['0', '1', '2', '4', '6'] 2^4 = 16 (+leading zero)
    ['1', '2', '8', '9'] 9^2 = 81
    ['0', '1', '2', '8', '9'] 9^2 = 81 (+leading zero)
    ['1', '3', '4', '8'] 3^4 = 81
    ['0', '1', '3', '4', '8'] 3^4 = 81 (+leading zero)
    ['2', '3', '6', '7', '9'] 3^6 = 729
    ['0', '2', '3', '6', '7', '9'] 3^6 = 729 (+leading zero)
    ['2', '3', '8'] 2^3 = 8
    ['0', '2', '3', '8'] 2^3 = 8 (+leading zero)
    ['2', '3', '9'] 3^2 = 9
    ['0', '2', '3', '9'] 3^2 = 9 (+leading zero)
    ['2', '4', '6', '8'] 8^2 = 64
    ['0', '2', '4', '6', '8'] 8^2 = 64 (+leading zero)
    ['2', '4', '7', '9'] 7^2 = 49
    ['0', '2', '4', '7', '9'] 7^2 = 49 (+leading zero)

Keiner von ihnen entspricht dem Problem (was auch an dem Fehlen von '0', '1', ..., '9' zu erkennen ist).

Der Beispielcode, der es löst, folgt. Beachten Sie auch, dass es in Python geschrieben ist, nicht weil es Ganzzahlen mit willkürlicher Genauigkeit benötigt (der Code berechnet nichts Größeres als 98 Millionen), sondern weil wir herausgefunden haben, dass die Anzahl der Tests so gering ist, dass wir eine Hochsprache verwenden sollten Nutzen Sie die eingebauten Container und Bibliotheken (beachten Sie auch: Der Code hat 28 Zeilen).

    import math

    m = 98765432
    l = []
    for i in xrange(2, 98765432):
        inv = 1.0/i
        r = m**inv
        if (r < 2.0): break
        top = int(math.floor(r))
        assert(top <= m)

        for j in xrange(2, top+1):
            s = str(i) + str(j) + str(j**i)
            l.append((sorted(s), i, j, j**i))
            assert(j**i <= m)

    l.sort()
    for s, i, j, ji in l:
        assert(ji <= m)
        ss = sorted(set(s))
        if s == ss:
            print '%s %d^%d = %d' % (s, i, j, ji)

        # Try with non significant zero somewhere
        s = ['0'] + s
        ss = sorted(set(s))
        if s == ss:
            print '%s %d^%d = %d (+leading zero)' % (s, i, j, ji)
24
hdante

Hier ist eine "nicht tragbare" Lösung für die Frage. Die Intel x86- und x64-CPUs haben das sogenannte EFLAGS-Register ( http://en.wikipedia.org/wiki/EFLAGS ), das vom Prozessor nach jeder ganzzahligen Rechenoperation gefüllt wird. Ich werde hier eine detaillierte Beschreibung überspringen. Die relevanten Flags sind das "Overflow" Flag (Maske 0x800) und das "Carry" Flag (Maske 0x1). Um sie richtig zu interpretieren, sollte man überlegen, ob die Operanden vom Typ mit oder ohne Vorzeichen sind.

Hier ist eine praktische Möglichkeit, die Flags in C/C++ zu überprüfen. Der folgende Code funktioniert in Visual Studio 2005 oder neuer (sowohl 32- als auch 64-Bit) sowie in GNU C/C++ 64-Bit.

#include <cstddef>
#if defined( _MSC_VER )
#include <intrin.h>
#endif

inline size_t query_intel_x86_eflags( const size_t query_bit_mask )
{
#if defined( _MSC_VER )
    return __readeflags() & query_bit_mask;
#Elif defined( __GNUC__ )
    // this code will work only on 64-bit GNU-C machines;
    // Tested and does NOT work with Intel C++ 10.1!
    size_t eflags;
    __asm__ __volatile__(
        "pushfq \n\t"
        "pop %%rax\n\t"
        "movq %%rax, %0\n\t"
        :"=r"(eflags)
        :
        :"%rax"
        );
    return eflags & query_bit_mask;
#else
#pragma message("No inline Assembly will work with this compiler!")
    return 0;
#endif
}

int main(int argc, char **argv)
{
    int x = 1000000000;
    int y = 20000;
    int z = x * y;
    int f = query_intel_x86_eflags( 0x801 );
    printf( "%X\n", f );
}

Wenn die Operanden ohne Überlauf multipliziert würden, würden Sie einen Rückgabewert von 0 von query_intel_eflags (0x801) erhalten, d. H., Weder die Übertrags- noch die Überlaufflags sind gesetzt. Im angegebenen Beispielcode von main () tritt ein Überlauf auf und die beiden Flags werden auf 1 gesetzt. Diese Überprüfung impliziert keine weiteren Berechnungen und sollte daher recht schnell sein.

23

Wenn Sie einen Datentyp haben, der größer ist als der, den Sie testen möchten (sagen Sie, Sie führen eine 32-Bit-Addition durch und Sie haben einen 64-Bit-Typ). Dies erkennt dann, ob ein Überlauf aufgetreten ist. Mein Beispiel ist für ein 8-Bit-Add. Kann aber vergrößert werden.

uint8_t x, y;   /* give these values */
const uint16_t data16   = x + y;
const bool carry        = (data16 > 0xff);
const bool overflow     = ((~(x ^ y)) & (x ^ data16) & 0x80);

Es basiert auf den auf dieser Seite erläuterten Konzepten: http://www.cs.umd.edu/class/spring2003/cmsc311/Notes/Comb/overflow.html

In einem 32-Bit-Beispiel werden 0xff zu 0xffffffff und 0x80 zu 0x80000000 und schließlich uint16_t zu uint64_t.

NOTE: Dies fängt ganzzahlige Additions-/Subtraktionsüberläufe auf, und ich erkannte, dass Ihre Frage eine Multiplikation beinhaltet. In diesem Fall ist die Aufteilung wahrscheinlich der beste Ansatz. Auf diese Weise stellen calloc Implementierungen in der Regel sicher, dass die Parameter nicht überlaufen, wenn sie multipliziert werden, um die endgültige Größe zu erhalten.

20
Evan Teran

Der einfachste Weg ist, Ihre unsigned longs in unsigned long longs umzuwandeln, zu multiplizieren und das Ergebnis mit 0x100000000LL zu vergleichen.

Sie werden wahrscheinlich feststellen, dass dies effizienter ist als die in Ihrem Beispiel beschriebene Aufteilung.

Oh, und es wird sowohl in C als auch in C++ funktionieren (da Sie die Frage mit beiden getaggt haben).


Ich habe mir gerade das glibc-Handbuch angesehen . Es gibt eine Erwähnung einer Ganzzahl-Überlauffalle (FPE_INTOVF_TRAP) als Teil von SIGFPE. Das wäre ideal, abgesehen von den üblen Dingen im Handbuch:

FPE_INTOVF_TRAP Integer-Überlauf (in einem C-Programm nur möglich, wenn Sie das Überlauf-Trapping hardwarespezifisch aktivieren).

Ein bisschen schade wirklich.

18

Obwohl es zwei Jahre her waren, hatte ich das Gefühl, dass ich meinen Penithworth genauso gut hinzufügen könnte, um einen Überlauf zumindest für Additionen schnell zu erkennen, was einen Anhaltspunkt für Multiplikation, Division und Potenz von geben könnte

Die Idee ist, dass genau deshalb, weil der Prozessor den Wert einfach auf Null zurücklaufen lässt und C/C++ von einem bestimmten Prozessor abstrahiert werden soll, Sie Folgendes tun können:

uint32_t x, y;
uint32_t value = x + y;
bool overflow = value < (x | y);

Auf diese Weise wird sichergestellt, dass ein Überlauf nicht fälschlicherweise erkannt wird, wenn ein Operand Null ist und einer nicht, und dies ist erheblich schneller als viele der zuvor vorgeschlagenen NOT/XOR/AND/-Test-Operationen.

Bearbeiten: Wie bereits erwähnt, ist dieser Ansatz, obwohl er besser ist als andere ausgefeilte Methoden, immer noch optimierbar. Das Folgende ist eine Überarbeitung des Originalcodes, der die Optimierung enthält:

uint32_t x, y;
uint32_t value = x + y;
bool overflow = value < x; // Alternatively "value < y" should also work
15
DX-MON

Überprüfen Sie bei Ganzzahlen ohne Vorzeichen nur, ob das Ergebnis kleiner als eines der folgenden Argumente ist:

unsigned int r, a, b;
r = a+b;
if (r < a)
{
    // overflow
}

Bei vorzeichenbehafteten ganzen Zahlen können Sie die Vorzeichen der Argumente und des Ergebnisses überprüfen. Ganzzahlen mit unterschiedlichen Vorzeichen können nicht überlaufen, und Ganzzahlen mit demselben Vorzeichen überlaufen nur, wenn das Ergebnis ein unterschiedliches Vorzeichen hat:

signed int r, a, b, s;
r = a+b;
s = a>=0;
if (s == (b>=0) && s != (r>=0))
{
    // overflow
}
14
anonymous

Sie können in C/C++ nicht auf das Überlauf-Flag zugreifen.

Bei einigen Compilern können Sie Trap-Anweisungen in den Code einfügen. Auf GCC ist die Option -ftrapv (aber ich muss zugeben, dass ich es nie benutzt habe. Werde es nach dem Posten überprüfen).

Das einzige tragbare und vom Compiler unabhängige Ding, das Sie tun können, ist, selbst nach Überläufen zu suchen. Genau wie du es in deinem Beispiel getan hast.

Bearbeiten:

Nur überprüft: -ftrapv scheint unter x86 mit dem neuesten GCC nichts zu tun. Vermutlich ist es ein Überbleibsel einer alten Version oder einer anderen Architektur. Ich hatte erwartet, dass der Compiler nach jedem Hinzufügen einen INTO-Opcode einfügt. Leider macht es das nicht.

13

Ich musste dieselbe Frage für Gleitkommazahlen beantworten, bei denen Bitmaskierung und -verschiebung nicht vielversprechend aussehen. Der Ansatz, den ich gewählt habe, funktioniert für vorzeichenbehaftete und vorzeichenlose Ganzzahlen und Gleitkommazahlen. Dies funktioniert auch dann, wenn kein größerer Datentyp für Zwischenberechnungen heraufgestuft werden muss. Es ist nicht für alle diese Typen am effizientesten, aber da es für alle funktioniert, lohnt es sich, es zu verwenden.

Vorzeichenbehafteter Überlauftest, Addition und Subtraktion:

  1. Erhalten Sie die Konstanten, die den größten und kleinstmöglichen Wert für den Typ MAXVALUE und MINVALUE darstellen.

  2. Berechnen und vergleichen Sie die Vorzeichen der Operanden.

    ein. Wenn einer der Werte Null ist, kann weder Addition noch Subtraktion überlaufen. Übrige Tests überspringen.

    b. Wenn die Zeichen entgegengesetzt sind, kann die Zugabe nicht überlaufen. Übrige Tests überspringen.

    c. Wenn die Vorzeichen gleich sind, kann die Subtraktion nicht überlaufen. Übrige Tests überspringen.

  3. Auf positiven Überlauf von MAXVALUE prüfen.

    ein. Wenn beide Vorzeichen positiv sind und MAXVALUE - A <B, läuft die Addition über.

    b. Wenn das Vorzeichen von B negativ ist und MAXVALUE - A <-B, läuft die Subtraktion über.

  4. Auf negativen Überlauf von MINVALUE prüfen.

    ein. Wenn beide Vorzeichen negativ sind und MINVALUE - A> B, läuft die Addition über.

    b. Wenn das Vorzeichen von A negativ ist und MINVALUE - A> B, läuft die Subtraktion über.

  5. Ansonsten kein Überlauf.

Signierter Überlauftest, Multiplikation und Division:

  1. Erhalten Sie die Konstanten, die den größten und kleinstmöglichen Wert für den Typ MAXVALUE und MINVALUE darstellen.

  2. Berechnen und vergleichen Sie die Beträge (Absolutwerte) der Operanden mit eins. (Im Folgenden wird angenommen, dass A und B diese Größen sind, nicht die signierten Originale.)

    ein. Wenn einer der Werte Null ist, kann die Multiplikation nicht überlaufen, und die Division ergibt Null oder Unendlich.

    b. Wenn einer der Werte eins ist, können Multiplikation und Division nicht überlaufen.

    c. Wenn die Größe eines Operanden unter dem einen und die des anderen Operanden über dem anderen liegt, kann die Multiplikation nicht überlaufen.

    d. Wenn beide Beträge kleiner als eins sind, kann die Division nicht überlaufen.

  3. Auf positiven Überlauf von MAXVALUE prüfen.

    ein. Wenn beide Operanden größer als eins sind und MAXVALUE/A <B, läuft die Multiplikation über.

    b. Wenn B kleiner als eins ist und MAXVALUE * B <A, läuft die Division über.

  4. Ansonsten kein Überlauf.

Hinweis: Der minimale Überlauf von MINVALUE wird von 3 verarbeitet, da wir absolute Werte angenommen haben. Wenn jedoch ABS (MINVALUE)> MAXVALUE ist, treten einige seltene Fehlalarme auf.

Die Unterlaufprüfungen sind ähnlich, beinhalten jedoch EPSILON (die kleinste positive Zahl größer als Null).

11
Paul Chernoch

CERT hat einen neuen Ansatz zum Erkennen und Melden von vorzeichenbehafteten Ganzzahlüberläufen, vorzeichenlosen Ganzzahlumbrüchen und Abschneiden von Ganzzahlen unter Verwendung des AIR-Ganzzahlmodells ("as-if") entwickelt. CERT hat einen technischen Bericht veröffentlicht, der das Modell beschreibt, und einen funktionierenden Prototyp auf der Basis von GCC 4.4.0 und GCC 4.5.0 erstellt.

Das AIR-Ganzzahlmodell erzeugt entweder einen Wert, der einem Wert entspricht, der unter Verwendung von Ganzzahlen mit unendlich vielen Bereichen erhalten worden wäre, oder führt zu einer Verletzung der Laufzeitbeschränkungen. Im Gegensatz zu früheren Ganzzahlmodellen erfordern AIR-Ganzzahlen keine präzisen Überfüllungen und unterbrechen oder hemmen daher die meisten vorhandenen Optimierungen nicht.

8

Ein weiteres interessantes Tool: http://embed.cs.utah.edu/ioc/

Dies ist ein gepatchter clang -Compiler, der dem Code beim Kompilieren Überprüfungen hinzufügt. So erhalten Sie eine Ausgabe, die folgendermaßen aussieht:

CLANG ARITHMETIC UNDEFINED at <add.c, (9:11)> :
Op: +, Reason : Signed Addition Overflow, 
BINARY OPERATION: left (int32): 2147483647 right (int32): 1
8

Eine andere Variante der Lösung mit Assembler ist eine externe Prozedur. Dieses Beispiel für vorzeichenlose Ganzzahlmultiplikation mit g ++ und fasm unter Linux x64.

Diese Prozedur multipliziert zwei vorzeichenlose Ganzzahlargumente (32 Bit) (gemäß Spezifikation für AMD64 (Abschnitt 3.2.3 Parameterübergabe).

Wenn die Klasse INTEGER ist, wird das nächste verfügbare Register der Sequenz% rdi,% rsi,% rdx,% rcx,% r8 und% r9 verwendet

(edi und esi werden in meinem Code registriert) und geben das Ergebnis oder 0 zurück, wenn ein Überlauf aufgetreten ist.

format ELF64

section '.text' executable 

public u_mul

u_mul:
  MOV eax, edi
  mul esi
  jnc u_mul_ret
  xor eax, eax
u_mul_ret:
ret

prüfung:

extern "C" unsigned int u_mul(const unsigned int a, const unsigned int b);

int main() {
    printf("%u\n", u_mul(4000000000,2));//0
    printf("%u\n", u_mul(UINT_MAX/2,2));//ok
    return 0;
}

programm mit ASM-Objektdatei verknüpfen. In meinem Fall in Qt Creator fügen Sie es zu LIBS in einer .pro-Datei hinzu

7
bartolo-otrit

Probieren Sie dieses Makro aus, um das Überlaufbit von 32-Bit-Computern zu testen (angepasst an die Lösung von Angel Sinigersky).

#define overflowflag(isOverflow){   \
size_t eflags;                      \
asm ("pushfl ;"                     \
     "pop %%eax"                    \
    : "=a" (eflags));               \
isOverflow = (eflags >> 11) & 1;}

Ich habe es als Makro definiert, da sonst das Überlaufbit überschrieben worden wäre.

Anschließend folgt eine kleine Anwendung mit dem obigen Codesegment:

#include <cstddef>
#include <stdio.h>
#include <iostream>
#include <conio.h>
#if defined( _MSC_VER )
#include <intrin.h>
#include <oskit/x86>
#endif

using namespace std;

#define detectOverflow(isOverflow){     \
size_t eflags;                      \
asm ("pushfl ;"                     \
    "pop %%eax"                     \
    : "=a" (eflags));               \
isOverflow = (eflags >> 11) & 1;}

int main(int argc, char **argv) {

    bool endTest = false;
    bool isOverflow;

    do {
        cout << "Enter two intergers" << endl;
        int x = 0;
        int y = 0;
        cin.clear();
        cin >> x >> y;
        int z = x * y;
        detectOverflow(isOverflow)
        printf("\nThe result is: %d", z);
        if (!isOverflow) {
            std::cout << ": no overflow occured\n" << std::endl;
        } else {
            std::cout << ": overflow occured\n" << std::endl;
        }

        z = x * x * y;
        detectOverflow(isOverflow)
        printf("\nThe result is: %d", z);
        if (!isOverflow) {
            std::cout << ": no overflow ocurred\n" << std::endl;
        } else {
            std::cout << ": overflow occured\n" << std::endl;
        }

        cout << "Do you want to stop? (Enter \"y\" or \"Y)" << endl;

        char c = 0;

        do {
            c = getchar();
        } while ((c == '\n') && (c != EOF));

        if (c == 'y' || c == 'Y') {
            endTest = true;
        }

        do {
            c = getchar();
        } while ((c != '\n') && (c != EOF));

    } while (!endTest);
}
5

Berechnen Sie die Ergebnisse mit Doppel. Sie haben 15 signifikante Stellen. Ihre Anforderung hat eine feste Obergrenze für c von 108- Es kann höchstens 8 Stellen haben. Daher ist das Ergebnis genau, wenn es sich in Reichweite befindet, und es läuft ansonsten nicht über.

4
MSalters

Sie können in C/C++ nicht auf das Überlauf-Flag zugreifen.

Dem stimme ich nicht zu. Sie könnten Inline-Asm schreiben und eine Anweisung jo (Sprungüberlauf) verwenden, vorausgesetzt, Sie befinden sich auf x86, um den Überlauf abzufangen. Natürlich wäre Ihr Code nicht mehr auf andere Architekturen übertragbar.

schau dir info as und info gcc an.

3
Tarski

Abfangen von Integer-Überläufen in C verweist auf eine allgemeinere Lösung als die von CERT diskutierte (es ist allgemeiner in Bezug auf behandelte Typen), auch wenn einige GCC-Erweiterungen erforderlich sind (ich weiß nicht wie) weithin unterstützt werden sie).

2
Blaisorblade

der x86-Befehlssatz enthält einen vorzeichenlosen Multiplikationsbefehl, der das Ergebnis in zwei Registern speichert. Um diese Anweisung von C zu verwenden, kann man folgenden Code in ein 64-Bit-Programm (gcc) schreiben:

unsigned long checked_imul(unsigned long a, unsigned long b) {
  __int128 res = (__int128)a * (__int128)b;
  if ((unsigned long)(res >> 64))
    printf("overflow in integer multiply");
  return (unsigned long)res;
}

Für 32-Bit-Programme müssen 64-Bit-Ergebnisse und 32-Bit-Parameter festgelegt werden.

Alternativ können Sie compilerabhängige Instinkte verwenden, um das Flagregister zu überprüfen. Die GCC-Dokumentation für Überlaufinstinkte finden Sie unter https://gcc.gnu.org/onlinedocs/gcc/Integer-Overflow-Builtins.html

0
Pauli Nieminen

mozilla::CheckedInt<T> liefert eine überlaufgeprüfte Ganzzahl-Mathematik für den Ganzzahl-Typ T (unter Verwendung der Compiler-Eigenheiten für clang und gcc, sofern verfügbar). Der Code ist unter MPL 2.0 und hängt von drei ( IntegerTypeTraits.h , Attributes.h und Compiler.h ab. Andere Header-spezifische Nicht-Standard-Bibliotheks-Header sowie Mozilla-spezifisches Zusicherungsmaschinerie . Sie möchten wahrscheinlich die Zusicherungsmaschinerie ersetzen, wenn Sie den Code importieren.

0
hsivonen

Eine saubere Möglichkeit wäre, alle Operatoren (insbesondere + und *) zu überschreiben und vor dem Ausführen der Operationen auf einen Überlauf zu prüfen.

0
Brian R. Bondy

Um die Antwort von Head Geek zu erweitern, gibt es einen schnelleren Weg, den addition_is_safe auszuführen.

bool addition_is_safe(unsigned int a, unsigned int b)
{
    unsigned int L_Mask = std::numeric_limits<unsigned int>::max();
    L_Mask >>= 1;
    L_Mask = ~L_Mask;

    a &= L_Mask;
    b &= L_Mask;

    return ( a == 0 || b == 0 );
}

Diese Methode ist maschinenarchitektursicher, da 64-Bit- und 32-Bit-Ganzzahlen ohne Vorzeichen weiterhin einwandfrei funktionieren. Grundsätzlich erstelle ich eine Maske, mit der alle bis auf das höchstwertige Bit ausgeblendet werden. Dann maskiere ich beide Ganzzahlen, und wenn für eine von ihnen das Bit nicht gesetzt ist, ist die Addition sicher.

Dies ist sogar noch schneller, wenn Sie die Maske in einem Konstruktor vorinitialisieren, da sie sich nie ändert.

0
Steztric