web-dev-qa-db-fra.com

Comment détecter un débordement multiplié d'entier non signé?

J'écrivais un programme en C++ pour trouver toutes les solutions de a b  = c , où a , b et c ensemble utilise tous les chiffres 0 à 9 exactement une fois. Le programme a bouclé sur les valeurs de a et b , et il a exécuté une routine de comptage de chiffres à chaque fois a , b et ab pour vérifier si la condition de chiffres était remplie.

Cependant, des solutions parasites peuvent être générées lorsque a b  déborde la limite de nombre entier. J'ai fini par vérifier cela en utilisant un code comme:

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

Existe-t-il un meilleur moyen de tester le débordement? Je sais que certaines puces ont un indicateur interne qui est défini en cas de débordement, mais je ne l’ai jamais vu y accéder en C ou C++.


Attention: signé int overflow est un comportement indéfini en C et C++ , et donc vous doivent le détecter sans le causer réellement. Pour un dépassement de capacité signé avant addition, voir Détection de dépassement de capacité signé en C/C++.

580
Chris Johnson

Je vois que vous utilisez des entiers non signés. Par définition, en C (je ne connais pas le C++), l'arithmétique non signée ne déborde pas ... donc, du moins pour C, votre point est sans intérêt :)

Avec les entiers signés, une fois qu'il y a eu dépassement, comportement non défini s'est produit et votre programme peut tout faire (par exemple: rendre les tests non concluants).

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

Pour créer un programme conforme, vous devez tester le débordement avant de générer ledit débordement. La méthode peut aussi être utilisée avec des entiers non signés

// 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 */;

pour la division (sauf pour le cas particulier INT_MIN et -1), il n'est pas possible de dépasser INT_MIN ou INT_MAX.

203
pmg

Il y a is un moyen de déterminer si une opération est susceptible de déborder, en utilisant les positions du bit le plus significatif dans les opérandes et un peu de connaissance binaire-mathématique de base.

En outre, deux opérandes quelconques donneront (au plus) un bit de plus que le bit le plus élevé de l'opérande le plus grand. Par exemple:

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);
}

Pour la multiplication, deux opérandes quelconques donneront (au plus) la somme des bits des opérandes. Par exemple:

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);
}

De même, vous pouvez estimer la taille maximale du résultat de a à la puissance de b comme ceci:

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

(Remplacez bien sûr le nombre de bits par votre entier cible.)

Je ne suis pas sûr du moyen le plus rapide de déterminer la position du bit le plus élevé, voici une méthode par force brute:

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

Ce n'est pas parfait, mais cela vous donnera une bonne idée si deux chiffres peuvent déborder avant l'opération. Je ne sais pas si ce serait plus rapide que de simplement vérifier le résultat comme vous l'avez suggéré, à cause de la boucle dans la fonction highestOneBitPosition, mais c'est peut-être le cas (surtout si vous saviez combien de bits se trouvaient auparavant dans les opérandes) .

164
Head Geek

Clang 3.4 + et GCC 5 + offrent des options intégrées d'arithmétique vérifiées. Ils offrent une solution très rapide à ce problème, surtout par rapport aux contrôles de sécurité par test de bits.

Pour l'exemple dans la question d'OP, cela fonctionnerait comme ça:

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
}

La documentation Clang ne spécifie pas si c_test contient le résultat dépassé si un débordement se produit, mais la documentation GCC l'indique. Etant donné que ces deux-là aiment être __builtin- compatibles, il est probablement prudent de supposer que c'est la même chose avec Clang.

Il existe un __builtin pour chaque opération arithmétique pouvant déborder (addition, soustraction, multiplication), avec des variantes signées et non signées, pour les tailles int, les tailles longues et les tailles longues. La syntaxe pour le nom est __builtin_[us](operation)(l?l?)_overflow:

  • u pour non signé ou s pour signé
  • l'opération est l'une de add, sub ou mul;
  • no l suffixe signifie que les opérandes sont ints; un l signifie long; deux ls signifient long long.

Donc, pour une addition d'entiers longs signés cochée, ce serait __builtin_saddl_overflow. La liste complète se trouve sur le page de documentation Clang .

GCC 5+ et Clang 3.8+ proposent en outre des fonctions génériques qui fonctionnent sans spécifier le type des valeurs: __builtin_add_overflow, __builtin_sub_overflow et __builtin_mul_overflow. Ceux-ci fonctionnent également sur des types plus petits que int.

Les éléments intégrés inférieurs à ce qui est le mieux pour la plate-forme. Sur x86, ils vérifient les indicateurs de retenue, de débordement et de signature.

Le fichier cl.exe de Visual Studio n'a pas d'équivalent direct. Pour les additions et les soustractions non signées, inclure <intrin.h> vous permettra d'utiliser addcarry_uNN et subborrow_uNN (où NN est le nombre de bits, comme addcarry_u8 ou subborrow_u64) . Leur signature est un peu obtuse:

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 est l'indicateur de report/emprunt en entrée, la valeur de retour est le report/emprunt en sortie. Il ne semble pas avoir d’équivalent pour les opérations signées ou les multiplications.

Sinon, Clang pour Windows est maintenant prêt pour la production (suffisant pour Chrome), ce qui pourrait également être une option.

136
zneak

Certains compilateurs permettent d'accéder à l'indicateur de dépassement d'entier dans la CPU que vous pouvez ensuite tester, mais ce n'est pas standard.

Vous pouvez également tester la possibilité de débordement avant d'effectuer la multiplication:

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

Avertissement: GCC peut optimiser l’absence de contrôle de débordement lors de la compilation avec -O2. L'option -Wall vous donnera un avertissement dans certains cas, comme

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

mais pas dans cet exemple:

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

Le seul moyen sûr est de vérifier le débordement avant qu'il ne se produise, comme décrit dans le papier CERT , ce qui serait extrêmement fastidieux à utiliser systématiquement.

Compiler avec -fwrapv résout le problème mais désactive certaines optimisations.

Nous avons désespérément besoin d'une meilleure solution. Je pense que le compilateur devrait émettre un avertissement par défaut lors d’une optimisation reposant sur le dépassement de capacité. La situation actuelle permet au compilateur d’optimiser un contrôle de débordement, ce qui est inacceptable à mon avis.

38
A Fog

clang prend désormais en charge les contrôles de débordement dynamiques pour les entiers signés et non signés. Voir - fsanitize = integer switch. Pour l'instant, ce n'est qu'un compilateur C++ avec une vérification de dépassement de capacité dynamique entièrement prise en charge à des fins de débogage.

30
ZAB

Je vois que beaucoup de gens ont répondu à la question sur le débordement, mais je voulais aborder son problème initial. Il a dit que le problème était de trouver unb= c tels que tous les chiffres sont utilisés sans répétition. Ok, ce n’est pas ce qu’il a demandé dans ce billet, mais je pense toujours qu’il était nécessaire d’étudier la limite supérieure du problème et de conclure qu’il n’aurait jamais besoin de calculer ou de détecter un débordement (remarque: je ne suis pas compétent en mathématiques, j’ai donc procédé pas à pas, mais le résultat final était si simple qu’il pouvait avoir une formule simple).

Le point principal est que la limite supérieure requise par le problème pour a, b ou c est 98.765.432. Quoi qu’il en soit, commençons par diviser le problème en parties triviales et non triviales:

  • x == 1 (toutes les permutations de 9, 8, 7, 6, 5, 4, 3, 2 sont des solutions)
  • x1 == x (pas de solution possible)
  • b == 0 (pas de solution possible)
  • 1b == 1 (pas de solution possible)
  • uneb, a> 1, b> 1 (non trivial)

Maintenant, nous devons simplement montrer qu’aucune autre solution n’est possible et que seules les permutations sont valides (et que le code pour les imprimer est trivial). Nous revenons à la limite supérieure. En fait, la limite supérieure est c ≤ 98.765.432. C'est la limite supérieure parce que c'est le plus grand nombre avec 8 chiffres (10 chiffres au total moins 1 pour chaque a et b). Cette limite supérieure est uniquement pour c car les bornes pour a et b doivent être beaucoup plus basses en raison de la croissance exponentielle, comme on peut le calculer, en variant b de 2 à la limite supérieure:

    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

Remarquez, par exemple, la dernière ligne: il est écrit que 1.97 ^ 27 ~ 98M. Ainsi, par exemple, 1 ^ 27 == 1 et 2 ^ 27 == 134.217.728 et ce n'est pas une solution car il a 9 chiffres (2> 1.97, donc il est en fait plus grand que ce qui devrait être testé). Comme on peut le constater, les combinaisons disponibles pour tester a et b sont vraiment petites. Pour b == 14, nous devons essayer 2 et 3. Pour b == 3, nous commençons à 2 et nous arrêtons à 462. Tous les résultats obtenus sont inférieurs à ~ 98M.

Il suffit maintenant de tester toutes les combinaisons ci-dessus et de rechercher celles qui ne répètent aucun chiffre:

    ['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)

Aucun d'entre eux ne correspond au problème (ce qui peut également être vu par l'absence de '0', '1', ..., '9').

L'exemple de code qui le résout suit. Notez également que cela est écrit en python, non pas parce qu'il nécessite des entiers à précision arbitraire (le code ne calcule rien de plus grand que 98 millions), mais parce que nous avons découvert que le nombre de tests est si petit qu'il faut utiliser un langage de haut niveau pour utiliser ses conteneurs et ses bibliothèques intégrés (remarque: le code a 28 lignes).

    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

Voici une solution "non portable" à la question. Les processeurs Intel x86 et x64 ont le registre EFLAGS ( http://en.wikipedia.org/wiki/EFLAGS ), qui est rempli par le processeur après chaque opération arithmétique entière. Je vais sauter une description détaillée ici. Les indicateurs pertinents sont l’indicateur "Débordement" (masque 0x800) et l’indicateur "Carry" (masque 0x1). Pour les interpréter correctement, il convient de déterminer si les opérandes sont de type signé ou non signé.

Voici un moyen pratique de vérifier les drapeaux de C/C++. Le code suivant fonctionnera sous Visual Studio 2005 ou version ultérieure (32 et 64 bits), ainsi que sur GNU C/C++ 64 bits.

#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 );
}

Si les opérandes étaient multipliés sans dépassement, vous obtiendriez une valeur de retour de 0 provenant de query_intel_eflags (0x801), ce qui signifie que ni les indicateurs de retenue ni de dépassement ne sont définis. Dans l'exemple de code fourni de main (), un débordement se produit et les deux indicateurs sont réglés sur 1. Cette vérification n'implique aucun autre calcul, elle devrait donc être assez rapide.

23
Angel Sinigersky

Si vous avez un type de données plus gros que celui que vous voulez tester (disons que vous faites une addition 32 bits et que vous avez un type 64 bits). Ensuite, cela détectera si un débordement s'est produit. Mon exemple est pour une addition de 8 bits. Mais peut être augmenté.

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);

Il est basé sur les concepts expliqués sur cette page: http://www.cs.umd.edu/class/spring2003/cmsc311/Notes/Comb/overflow.html

Pour un exemple 32 bits, 0xff devient 0xffffffff et 0x80 devient 0x80000000 et enfin uint16_t devient uint64_t.

NOTE: cela capture les débordements d'addition/soustraction d'entiers, et j'ai réalisé que votre question impliquait une multiplication. Dans ce cas, la division est probablement la meilleure approche. C'est généralement une façon pour les implémentations de calloc de s'assurer que les paramètres ne débordent pas, car ils sont multipliés pour obtenir la taille finale.

20
Evan Teran

La méthode la plus simple consiste à convertir votre unsigned longs en unsigned long longs, à effectuer votre multiplication et à comparer le résultat à 0x100000000LL.

Vous constaterez probablement que cela est plus efficace que la division, comme vous l'avez fait dans votre exemple.

Oh, et ça fonctionnera à la fois en C et en C++ (car vous avez balisé la question avec les deux).


Je viens de jeter un coup d'oeil au manuel de la glibc . Il est fait mention d'une interruption de débordement d'entier (FPE_INTOVF_TRAP) dans le cadre de SIGFPE. Ce serait idéal, mis à part les passages désagréables du manuel:

FPE_INTOVF_TRAP Débordement d'entier (impossible dans un programme C à moins d'activer le recouvrement de débordement de manière spécifique au matériel).

Un peu dommage, vraiment.

18
Andrew Edgecombe

Bien que cela fait deux ans, j’ai pensé que je pourrais aussi bien ajouter mon identifiant pour un moyen très rapide de détecter les débordements pour au moins les ajouts, ce qui pourrait donner une longueur d’avance en termes de multiplication, de division et de puissance.

L'idée est que, précisément parce que le processeur laisse simplement la valeur revenir à zéro et que C/C++ doit être extrait de tout processeur spécifique, vous pouvez:

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

Cela garantit à la fois que si un opérande vaut zéro et qu'un autre ne l'est pas, le dépassement de capacité ne sera pas détecté de manière erronée et sera beaucoup plus rapide que beaucoup d'opérations NOT/XOR/AND/test, comme suggéré précédemment.

Edit: Comme indiqué, cette approche, bien que meilleure que d’autres méthodes plus élaborées, reste optimisable. Voici une révision du code d'origine contenant l'optimisation:

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

Pour les entiers non signés, vérifiez que le résultat est inférieur à l'un des arguments:

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

Pour les entiers signés, vous pouvez vérifier les signes des arguments et du résultat. les entiers de signes différents ne peuvent pas déborder, et les entiers du même signe dépassent uniquement si le résultat est de signe différent:

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

Vous ne pouvez pas accéder à l'indicateur de débordement à partir de C/C++.

Certains compilateurs vous permettent d'insérer des instructions d'interruption dans le code. Sur GCC, l'option est -ftrapv (mais je dois admettre que je ne l'ai jamais utilisée. Je la vérifierai après la publication).

La seule chose que vous pouvez faire, indépendamment du compilateur, est de vérifier vous-même les débordements. Tout comme vous l'avez fait dans votre exemple.

Edit:

Je viens de vérifier: -ftrapv semble ne rien faire sur x86 en utilisant le dernier GCC. Suppose que c'est un reste d'une ancienne version ou spécifique à une autre architecture. Je m'attendais à ce que le compilateur insère un opcode INTO après chaque addition. Malheureusement, cela ne se fait pas.

13
Nils Pipenbrinck

Je devais répondre à cette même question pour les nombres en virgule flottante, où le masquage et le décalage des bits ne semblent pas prometteurs. L'approche que j'ai choisie fonctionne pour les nombres signés et non signés, les nombres entiers et les nombres à virgule flottante Cela fonctionne même s'il n'y a pas de type de données plus important à promouvoir pour les calculs intermédiaires. Ce n'est pas le plus efficace pour tous ces types, mais comme il fonctionne pour tous, il vaut la peine de l'utiliser.

Test de débordement signé, addition et soustraction:

  1. Obtenez les constantes représentant les valeurs les plus grandes et les plus petites possibles pour le type MAXVALUE et MINVALUE.

  2. Calculez et comparez les signes des opérandes.

    une. Si l'une ou l'autre des valeurs est zéro, ni l'addition ni la soustraction ne peuvent déborder. Passer les tests restants.

    b. Si les signes sont opposés, alors l'addition ne peut pas déborder. Passer les tests restants.

    c. Si les signes sont les mêmes, la soustraction ne peut déborder. Passer les tests restants.

  3. Testez le dépassement positif de MAXVALUE.

    une. Si les deux signes sont positifs et MAXVALUE - A <B, l’ajout débordera.

    b. Si le signe de B est négatif et que MAXVALUE - A <-B, la soustraction débordera.

  4. Testez le dépassement négatif de MINVALUE.

    une. Si les deux signes sont négatifs et MINVALUE - A> B, l’ajout débordera.

    b. Si le signe de A est négatif et que MINVALUE - A> B, la soustraction débordera.

  5. Sinon, pas de débordement.

Test de débordement signé, multiplication et division:

  1. Obtenez les constantes représentant les valeurs les plus grandes et les plus petites possibles pour le type MAXVALUE et MINVALUE.

  2. Calculez et comparez les magnitudes (valeurs absolues) des opérandes à un. (Supposons ci-dessous A et B ces grandeurs et non les originaux signés.)

    une. Si l'une ou l'autre des valeurs est zéro, la multiplication ne peut pas déborder et la division donne zéro ou un infini.

    b. Si l'une ou l'autre des valeurs est un, la multiplication et la division ne peuvent pas déborder.

    c. Si la magnitude d'un opérande est inférieure à l'un et que l'autre est supérieure à un, la multiplication ne peut pas déborder.

    ré. Si les magnitudes sont toutes deux inférieures à un, la division ne peut pas déborder.

  3. Testez le dépassement positif de MAXVALUE.

    une. Si les deux opérandes sont supérieurs à un et que MAXVALUE/A <B, la multiplication débordera.

    b. Si B est inférieur à un et MAXVALUE * B <A, la division débordera.

  4. Sinon, pas de débordement.

Remarque: Le dépassement de capacité minimal de MINVALUE est traité par 3, car nous avons pris des valeurs absolues. Cependant, si ABS (MINVALUE)> MAXVALUE, nous aurons quelques rares faux positifs.

Les tests de débordement sont similaires, mais impliquent EPSILON (le plus petit nombre positif supérieur à zéro).

11
Paul Chernoch

CERT a développé une nouvelle approche pour détecter et signaler les dépassements d’entiers signés, les retours à la ligne d’entiers non signés et la troncature d’entiers à l’aide du modèle entier "as-if" (AIR). Le CERT a publié un rapport technique décrivant le modèle et produit un prototype fonctionnel basé sur GCC 4.4.0 et GCC 4.5.0.

Le modèle entier AIR génère une valeur équivalente à celle qui aurait été obtenue à l'aide d'entiers infinis, ou génère une violation de contrainte d'exécution. Contrairement aux modèles entiers précédents, les entiers AIR ne nécessitent pas de traps précis et ne brisent donc ni n'empêchent la plupart des optimisations existantes.

8

Un autre outil intéressant: http://embed.cs.utah.edu/ioc/

Il s'agit d'un compilateur clang patché, qui ajoute des contrôles au code lors de la compilation. Donc, vous obtenez une sortie ressemblant à ceci:

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

Une autre variante de solution utilisant assembleur est une procédure externe. Cet exemple illustre la multiplication d’entiers non signés utilisant g ++ et fasm sous linux x64.

Cette procédure multiplie deux arguments entiers non signés (32 bits) (selon spécification pour AMD64 (section 3.2.3 Passage de paramètres).

Si la classe est INTEGER, le prochain registre disponible de la séquence% rdi,% rsi,% rdx,% rcx,% r8 et% r9 est utilisé

(edi et esi s'inscrivent dans mon code)) et renvoie le résultat ou 0 si un débordement s'est produit.

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

tester:

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;
}

programme de liaison avec le fichier objet asm. Dans mon cas, dans Qt Creator, ajoutez-le à LIBS dans un fichier .pro

7
bartolo-otrit

Essayez cette macro pour tester le bit de débordement des machines 32 bits (adapté à la solution d’Angel Sinigersky)

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

Je l'ai défini comme une macro car sinon le bit de débordement aurait été écrasé.

La suite est une petite application avec la séparation de code ci-dessus:

#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
Markus Demarmels

Calculez les résultats avec des doubles. Ils ont 15 chiffres significatifs. Votre exigence a une limite supérieure fixe sur c sur 108- il peut avoir au plus 8 chiffres. Par conséquent, le résultat sera précis s'il est dans la plage et il ne débordera pas autrement.

4
MSalters

Vous ne pouvez pas accéder à l'indicateur de débordement à partir de C/C++.

Je ne suis pas d'accord avec ça. Vous pouvez écrire un asm en ligne et utiliser une instruction jo (débordement de saut) en supposant que vous êtes sur x86 pour intercepter le débordement. Bien sûr, votre code ne serait plus portable pour d'autres architectures.

regardez info as et info gcc.

3
Tarski

Catching Integer Overflows in C indique une solution plus générale que celle évoquée par CERT (elle est plus générale en termes de types traités), même si elle nécessite des extensions GCC (je ne sais pas comment largement pris en charge, ils sont).

2
Blaisorblade

le jeu d'instructions x86 inclut une instruction de multiplication non signée qui stocke le résultat dans deux registres. Pour utiliser cette instruction en C, on peut écrire le code suivant dans un programme 64 bits (gcc):

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;
}

Pour un programme 32 bits, il faut obtenir un résultat de 64 bits et des paramètres de 32 bits.

La solution de rechange consiste à utiliser l'instinct du compilateur pour vérifier le registre des drapeaux. La documentation GCC pour les instincts de débordement peut être consultée à partir de https://gcc.gnu.org/onlinedocs/gcc/Integer-Overflow-Builtins.html

0
Pauli Nieminen

mozilla::CheckedInt<T> fournit des calculs d'entiers vérifiés par dépassement de capacité pour le type entier T (en utilisant les propriétés intrinsèques du compilateur sur clang et gcc). Le code est sous MPL 2.0 et dépend de trois ( IntegerTypeTraits.h , Attributes.h et Compiler.h ) autres en-têtes de bibliothèque non standard d'en-tête plus spécifiques à Mozilla machines d'assertion . Vous voudrez probablement remplacer la machine d'assertion si vous importez le code.

0
hsivonen

Une façon propre de le faire serait de remplacer tous les opérateurs (+ et * en particulier) et de rechercher un débordement avant l'exécution des opérations.

0
Brian R. Bondy

Pour développer la réponse de Head Geek, il existe un moyen plus rapide de faire le addition_is_safe;

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 );
}

Ceci utilise un coffre-fort pour l'architecture de la machine, dans la mesure où les entiers non signés 64 bits et 32 ​​bits continueront à fonctionner correctement. Fondamentalement, je crée un masque qui masquera tout sauf le bit le plus significatif. Ensuite, je masque les deux entiers, et si l’un ou l’autre n’a pas défini ce bit, l’ajout est sécurisé.

Ce serait encore plus rapide si vous pré-initialisiez le masque dans un constructeur, car il ne change jamais.

0
Steztric