web-dev-qa-db-fra.com

Comparaisons signées / non signées

J'essaie de comprendre pourquoi le code suivant n'émet pas d'avertissement à l'endroit indiqué.

//from limits.h
#define UINT_MAX 0xffffffff /* maximum unsigned int value */
#define INT_MAX  2147483647 /* maximum (signed) int value */
            /* = 0x7fffffff */

int a = INT_MAX;
//_int64 a = INT_MAX; // makes all warnings go away
unsigned int b = UINT_MAX;
bool c = false;

if(a < b) // warning C4018: '<' : signed/unsigned mismatch
    c = true;
if(a > b) // warning C4018: '<' : signed/unsigned mismatch
    c = true;
if(a <= b) // warning C4018: '<' : signed/unsigned mismatch
    c = true;
if(a >= b) // warning C4018: '<' : signed/unsigned mismatch
    c = true;
if(a == b) // no warning <--- warning expected here
    c = true;
if(((unsigned int)a) == b) // no warning (as expected)
    c = true;
if(a == ((int)b)) // no warning (as expected)
    c = true;

Je pensais que cela avait à voir avec la promotion en arrière-plan, mais les deux derniers semblent dire le contraire.

À mon avis, le premier == _ la comparaison est-elle autant une non-concordance signée/non signée que les autres?

77
Peter

Lors de la comparaison signée avec non signée, le compilateur convertit la valeur signée en non signée. Pour l'égalité, cela n'a pas d'importance, -1 == (unsigned) -1. Pour les autres comparaisons, par exemple, ce qui suit est vrai: -1 > 2U.

EDIT: Références:

5/9: (expressions)

De nombreux opérateurs binaires qui attendent des opérandes de type arithmétique ou énumération provoquent des conversions et génèrent des types de résultat de manière similaire. Le but est de générer un type commun, qui est également le type du résultat. Ce modèle s'appelle les conversions arithmétiques habituelles, définies comme suit:

  • Si l'un des opérandes est de type long double, l'autre doit être converti en long double.

  • Sinon, si l'un des opérandes est double, l'autre doit être converti en double.

  • Sinon, si l'un des opérandes est float, l'autre doit être converti en float.

  • Sinon, les promotions intégrales (4.5) doivent être effectuées sur les deux opérandes.54)

  • Ensuite, si l'un des opérandes est long non signé, l'autre doit être converti en long non signé.

  • Autrement, si un opérande est un long int et l'autre unsigned int, alors si un long int peut représenter toutes les valeurs d'un unsigned int, il doit être converti en long int; sinon, les deux opérandes doivent être convertis en unsigned long int.

  • Sinon, si l'un des opérandes est long, l'autre doit être converti en long.

  • Sinon, si l'un des opérandes est non signé, l'autre doit être converti en non signé.

4.7/2: (conversions intégrales)

Si le type de destination est non signé, la valeur résultante est le plus petit entier non signé conforme au nombre entier source (modulo 2n où n est le nombre de bits utilisés pour représenter le type non signé). [Remarque: dans la représentation du complément à deux, cette conversion est conceptuelle et le modèle de bits n’est pas modifié (s’il n’ya pas de troncature). ]

EDIT2: Niveaux d’avertissement MSVC

Ce qui est mis en garde sur les différents niveaux d’avertissement de MSVC, ce sont bien sûr les choix faits par les développeurs. Selon moi, leurs choix en ce qui concerne l'égalité signée/non signée par rapport aux comparaisons plus grandes/moins significatives sont tout à fait subjectifs, bien sûr:

-1 == -1 Signifie la même chose que -1 == (unsigned) -1 - Je trouve que c'est un résultat intuitif.

-1 < 2ne signifie pas signifie la même chose que -1 < (unsigned) 2 - Cela est moins intuitif à première vue, et IMO mérite un avertissement "plus tôt".

81
Erik

L'exemple suivant montre pourquoi les avertissements signés/non signés sont importants et que les programmeurs doivent y prêter attention.

Devinez la sortie de ce code?

#include <iostream>

int main() {
        int i = -1;
        unsigned int j = 1;
        if ( i < j ) 
            std::cout << " i is less than j";
        else
            std::cout << " i is greater than j";

        return 0;
}

Sortie:

i is greater than j

Surpris? Démo en ligne: http://www.ideone.com/5iCxY

Bottomline: en comparaison, si l'un des opérandes est unsigned, alors l'autre opérande est converti implicitement en unsigned si son type est signé!

27
Nawaz

L'opérateur == fait juste une comparaison bit à bit (par simple division pour voir si c'est 0).

Le plus petit/plus grand que les comparaisons reposent beaucoup plus sur le signe du nombre.

Exemple 4 bits:

1111 = 15? ou -1?

donc, si vous avez 1111 <0001 ... c'est ambigu ...

mais si vous avez 1111 == 1111 ... C'est la même chose, même si vous ne le vouliez pas.

3
Yochai Timmer

Dans un système qui représente les valeurs utilisant le complément à 2 (la plupart des processeurs modernes), elles sont égales, même sous leur forme binaire. C'est peut-être pourquoi le compilateur ne se plaint pas de a == b.

Et pour moi, c'est étrange compilateur ne vous avertit pas sur a == ((int) b). Je pense que cela devrait vous donner un avertissement de troncature entier ou quelque chose.

1
Hossein