Operación de comparación en enteros sin signo y con signo

8 minutos de lectura

avatar de usuario
Guantánamo

Ver este fragmento de código

int main()
{ 
 unsigned int a = 1000;
 int b = -1;
 if (a>b) printf("A is BIG! %d\n", a-b);
 else printf("a is SMALL! %d\n", a-b); 
 return 0;
}   

Esto da la salida: a es PEQUEÑO: 1001

No entiendo lo que está pasando aquí. ¿Cómo funciona aquí el operador >? ¿Por qué “a” es más pequeña que “b”? Si es más pequeño, ¿por qué obtengo un número positivo (1001) como diferencia?

  • Si usa el indicador del compilador -Wsign-compare, recibirá una advertencia para la comparación. Siempre debe usar -Wall (que incluye -Wsign-compare). Ver aquí para conocer otras formas de evitar este problema.

    – Alejandro

    5 de diciembre de 2010 a las 20:58


  • Consulte esta publicación para obtener más información: stackoverflow.com/q/10474769/844882

    – Adrián Monk

    7 de mayo de 2012 a las 1:44

  • @ Aleph7 – técnicamente eso no es del todo correcto, -Wsign-compare es solo inc. con -Pared Si Estás compilando C++. No está incluido para C (ver aquí gcc.gnu.org/onlinedocs/gcc/Warning-Options.html) Lo probé y puedo confirmar que el código anterior no da ninguna advertencia con -Wall pero sí con -Wsign-compare (estoy usando gcc (Ubuntu 5.2.1-22ubuntu2) 5.2.1 20151010)

    – bph

    10 de noviembre de 2016 a las 20:09


avatar de usuario
Hormiga

Las operaciones binarias entre diferentes tipos integrales se realizan dentro de un tipo “común” definido por los llamados conversiones aritméticas habituales (ver la especificación de idioma, 6.3.1.8). En su caso, el tipo “común” es unsigned int. Esto significa que int operando (su b) se convertirá en unsigned int antes de la comparación, así como con el fin de realizar la resta.

Cuando -1 se convierte en unsigned int el resultado es el máximo posible unsigned int valor (igual que UINT_MAX). No hace falta decir que va a ser mayor que su no firmado 1000 valor, lo que significa que a > b de hecho es falso y a es de hecho pequeña comparado con (unsigned) b. Él if en su código debe resolver a else rama, que es lo que observaste en tu experimento.

Las mismas reglas de conversión se aplican a la resta. Su a-b se interpreta realmente como a - (unsigned) b y el resultado tiene tipo unsigned int. Dicho valor no se puede imprimir con %d especificador de formato, ya que %d solo funciona con firmado valores. Su intento de imprimirlo con %d da como resultado un comportamiento indefinido, por lo que el valor que ve impreso (aunque tiene una explicación lógica determinista en la práctica) no tiene ningún sentido desde el punto de vista del lenguaje C.

Editar: En realidad, podría estar equivocado sobre la parte del comportamiento indefinido. De acuerdo con la especificación del lenguaje C, la parte común del rango del tipo de entero con signo y sin signo correspondiente debe tener una representación idéntica (lo que implica, según la nota al pie 31, “intercambiabilidad como argumentos para funciones”). Entonces, el resultado de a - b la expresión no está firmada 1001 como se describe arriba, y a menos que me falte algo, es legal imprimir este valor específico sin firmar con %d especificador, ya que cae dentro del rango positivo de int. Impresión (unsigned) INT_MAX + 1 con %d sería indefinido, pero 1001u está bien.

  • Aunque sabemos o podemos adivinar lo suficiente sobre la convención de llamada de su implementación para concluir que lo que sucedió es el resultado sin signo de ab, que de hecho es 1001, pasó a través de los varargs ileso y se reinterpretó como firmado sin cambiar el valor.

    –Steve Jessop

    18 de enero de 2010 a las 10:49


  • Sí, pasando int sin firmar y haciendo va_arg(ap, int) solo no es UB todavía. Pero de hecho es UB violar los requisitos de printf al esperar un int. Aunque me parece una tontería. ¿Por qué no han especificado para printf: “El tipo del siguiente argumento será un int con o sin signo, y estará dentro del rango de int”.

    – Johannes Schaub – litb

    18 de enero de 2010 a las 11:13


  • @Johannes: En realidad, es posible que ya esté especificado. Ver mi edición.

    – Ant

    18 de enero de 2010 a las 15:47

  • Pero si tiene una representación compatible no es importante, creo. Si el estándar dice que algo es UB, entonces es UB: no está reemplazado por alguna declaración en una nota al pie. Creo que esto solo significa que hay cierto rango de libertad que puede hacer, sin requisitos estrictos por parte del estándar. Al igual que con la lectura de diferentes miembros del sindicato (creo que esto no es UB por adelantado en C, pero puede definirse bien si ambos miembros tienen representaciones compatibles). Pero llamar a alguna función a través de una expresión de puntero de función sin prototipo con argumentos incompatibles sigue siendo UB, por ejemplo.

    – Johannes Schaub – litb

    18 de enero de 2010 a las 17:49

  • En este caso, fprintf la descripción indica: (para %d): “El argumento int se convierte en…” y “Si algún argumento no es del tipo correcto para la especificación de conversión correspondiente, el comportamiento no está definido”. Así que no creo que esté bien definido. ¿Quizás alguien de Usenet lo sepa?

    – Johannes Schaub – litb

    18 de enero de 2010 a las 17:51

avatar de usuario
kennytm

En una implementación típica donde int es de 32 bits, -1 cuando se convierte a un unsigned int es 4,294,967,295 que es de hecho ≥ 1000.

Incluso si tratas la resta en un unsigned mundo, 1000 - (4,294,967,295) = -4,294,966,295 = 1,001 que es lo que obtienes.

Es por eso gcc escupirá una advertencia cuando compares unsigned con signed. (Si no ve una advertencia, pase el -Wsign-compare bandera.)

  • Voté negativamente por “4,294,967,295 (complemento de 2)”. No tiene nada que ver con el complemento a 2. Dará el mismo valor en una máquina de complemento a 1. Y producirá un valor diferente en un entero de ancho de bits diferente.

    – Johannes Schaub – litb

    18 de enero de 2010 a las 9:58

  • @Schaub: Tal vez no esté claro, pero lo que quiero decir es que 4,294,967,295 (que es el complemento de 2 de 1) es de hecho ≥1. Además, la máquina de complemento a 1 la representación de -1 es 4.294.967.294.

    – kennytm

    18 de enero de 2010 a las 10:09


  • como dice litb, no tiene nada que ver con la representación. En una máquina de complemento a 1, convertir -1 en resultados sin signo en UINT_MAX, no da como resultado que se reinterprete el patrón de bits de complemento a 1. Una de las varias formas en que el complemento a 2 es conveniente es que las conversiones C (no) firmadas no cambian el patrón de bits. Eso es particular del complemento a 2: las conversiones de C a tipos sin signo se definen en términos de aritmética de módulo, no en términos de patrón de bits. En complemento a 1, la implementación tiene que hacer algo de trabajo real para llegar a UINT_MAX.

    –Steve Jessop

    18 de enero de 2010 a las 10:56


  • la edición es mejor, pero aún así no hay garantía de que UINT_MAX es 4.294.967.295. Consulte también stackoverflow.com/questions/1863153

    – Alok Singhal

    18 de enero de 2010 a las 16:15


 #include<stdio.h>
 int main()
 {
   int a = 1000;
   signed int b = -1, c = -2;
   printf("%d",(unsigned int)b);
   printf("%d\n",(unsigned int)c);
   printf("%d\n",(unsigned int)a);

   if(1000>-1){
      printf("\ntrue");
   }
   else 
     printf("\nfalse");
     return 0;
 }

Para esto necesitas entender la precedencia de los operadores.

  1. Los operadores relacionales funcionan de izquierda a derecha… así que cuando se trata

    si (1000>-1)

luego, en primer lugar, cambiará -1 a un entero sin signo porque int se trata de forma predeterminada como un número sin signo y su rango es mayor que el número con signo

-1 cambiará al número sin firmar, cambia a un número muy grande

avatar de usuario
antti huima

Está haciendo una comparación sin signo, es decir, comparando 1000 con 2^32 – 1.

La salida está firmada debido a %d en printf.

NB: a veces, el comportamiento cuando mezcla operandos firmados y no firmados es específico del compilador. Creo que es mejor evitarlos y hacer yesos en caso de duda.

Encuentre una manera fácil de comparar, tal vez útil cuando no puede deshacerse de la declaración sin firmar (por ejemplo, [NSArray count]), simplemente fuerce el “int sin firmar” a un “int”.

Por favor, corríjame si estoy equivocado.

if (((int)a)>b) {
    ....
}

avatar de usuario
DigitalRoss

El hardware está diseñado para comparar firmado con firmado y sin firmar con sin firmar.

Si desea el resultado aritmético, primero convierta el valor sin signo a un tipo con signo más grande. De lo contrario, el compilador asumirá que la comparación es realmente entre valores sin signo.

Y -1 se representa como 1111..1111, por lo que es una cantidad muy grande… La más grande… Cuando se interpreta como sin signo.

avatar de usuario
naveen kumar

al comparar a>b donde a es de tipo int sin signo y b es de tipo int, b es tipo casted a unsigned int por lo tanto, el valor int firmado -1 se convierte en el valor MAX de unsigned**(rango: 0 a (2^32)-1)** Por lo tanto, a>b, es decir, (1000>4294967296) se vuelve falso. Por lo tanto, el bucle más printf(“a es PEQUEÑO! %d\n”, ab); ejecutado.

  • Este es un duplicado de menor esfuerzo de la respuesta más votada

    – emsimpson92

    09/07/2018 a las 23:00

¿Ha sido útil esta solución?