¿Asignar números negativos a un int sin signo?

8 minutos de lectura

avatar de usuario
ipkiss

En el lenguaje de programación C, unsigned int se utiliza para almacenar valores positivos solamente. Sin embargo, cuando ejecuto el siguiente código:

unsigned int x = -12;
printf("%d", x);

La salida sigue siendo -12. Pensé que debería haberse impreso: 12, ¿o estoy malinterpretando algo?

  • Estás mintiendo al compilador: le estás diciendo al compilador que le darás printf un int (los "%d"), pero en su lugar le das un unsigned. No le mientas al compilador.

    – pmg

    2 de marzo de 2011 a las 15:37


  • @Joachim: el compilador está haciendo las ‘conversiones aritméticas habituales’ (ver 3.2.1.5). “… De lo contrario, si cualquiera de los operandos tiene un tipo int sin signo, el otro operando se convierte a int sin signo …” y -12 convertido a resultados sin firmar en (UINT_MAX + 1) - 12.

    – pmg

    2 de marzo de 2011 a las 16:01

  • @Joachim: la bandera de gcc -Wsign-conversion (incluido en -Wconversion) se puede utilizar para advertir sobre esto. Como ya sabes, no es parte de -Wall o -Wextra.

    – pmg

    2 de marzo de 2011 a las 16:12


  • @Joachim: no veo una razón por la que el compilador deba emitir advertencias para un uso perfectamente razonable del lenguaje. Es una pena que los detalles de la semántica de enteros de C no se presenten al principio de los libros y cursos, pero eso no es realmente una excusa para adaptar el nivel de advertencia preferido del compilador a las personas que no conocen el lenguaje en el que están programando…

    – R.. GitHub DEJA DE AYUDAR A ICE

    3 de marzo de 2011 a las 0:54

  • @R: De hecho, es una pena que el CCG el compilador le miente al usuario con -Wallya que no activa “todas” las advertencias.

    – usuario541686

    3 de marzo de 2011 a las 7:49

avatar de usuario
tim oltrogge

los -12 a la derecha de su signo igual se configura como un entero con signo (probablemente de 32 bits de tamaño) y tendrá el valor hexadecimal 0xFFFFFFF4. El compilador genera código para mover este entero con signo a su entero sin firmar x que también es una entidad de 32 bits. El compilador asume que solo tiene un valor positivo a la derecha del signo igual, por lo que simplemente mueve los 32 bits a x. x ahora tiene el valor 0xFFFFFFF4 cual es 4294967284 si se interpreta como un número positivo. Pero el printf formato de %d dice que los 32 bits deben interpretarse como un entero con signo para que obtenga -12. si hubieras usado %u hubiera impreso como 4294967284.

En cualquier caso, no obtiene lo que esperaba, ya que el lenguaje C “confía” en el escritor del código para que solo solicite cosas “sensibles”. Esto es común en C. Si desea asignar un valor a x y no estaba seguro de si el valor en el lado derecho de los iguales era positivo, podría haber escrito unsigned int x = abs(-12); y obligó al compilador a generar código para tomar el valor absoluto de un entero con signo antes de moverlo al entero sin signo.

  • Gran parte de esto es técnicamente incorrecto. En unsigned int x = -12;los -12 es una expresión constante entera de tipo int. Eso se convierte en unsigned int sumando (o restando) repetidamente UINT_MAX + 1 al valor hasta un valor entre 0 y UINT_MAX (ambos inclusive), ese valor es el resultado de la conversión. En las máquinas de complemento a uno o de signo y magnitud, eso cambia el patrón de bits del valor; en las máquinas de complemento a dos más comunes, es solo una interpretación diferente del mismo patrón de bits.

    –Daniel Fischer

    24/06/2012 a las 19:30

  • Esa parte, sin embargo, está completamente definida por el estándar, y después unsigned int x = -12;El valor de x siempre debe ser UINT_MAX - 11. La siguiente línea, printf("%d", x); invoca un comportamiento indefinido, ya que el %d el especificador de conversión requiere una firma int y el valor de x es no representable en ambos unsigned int y int. El comportamiento común es, por supuesto, la reinterpretación del patrón de bits, en máquinas de complemento a uno que en este caso producirían -11en signo y magnitud con 32 bits ints, -2147483636. Y en las máquinas de complemento a dos el comportamiento observado.

    –Daniel Fischer

    24/06/2012 a las 19:30

  • @DanielFischer ¿Por qué el valor de x no representable en unsigned int ya que el valor es menor que UINT_MAX?

    – ajay

    25 de febrero de 2014 a las 10:33

  • @ajay El valor de x es representable como un unsigned intpero no como un int (a menos que la implementación sea tan extraña que su unsigned ints tienen un bit de relleno por lo que el rango es un subrango del int rango). Imprimiendo un unsigned int con el %d el especificador de conversión es un comportamiento indefinido a menos que el valor sea representable en ambas cosas tipos

    –Daniel Fischer

    25 de febrero de 2014 a las 11:18

  • Tiene sentido para mi. Anécdota personal: traté de eliminar el signo de un número entero convirtiéndolo en un número entero sin firmar y haciendo que almacenara el nuevo valor. Llegué a descubrir, por supuesto, que no es así como funciona C++.

    – Montana Burr

    11 de noviembre de 2016 a las 1:37

avatar de usuario
Preocupado binario

El int no está cantado, pero le has dicho printf para verlo como un int firmado.

Tratar

unsigned int x = -12; printf("%u", x);

No imprimirá “12”, pero imprimirá el valor máximo de un int sin signo menos 11.

El ejercicio para el lector es averiguar por qué 🙂

  • “11 o 12” está mal. El resultado está bien definido y es UINT_MAX-11no UINT_MAX-12.

    – R.. GitHub DEJA DE AYUDAR A ICE

    3 de marzo de 2011 a las 0:55

  • @R: No podía recordar si era max-11 o max – 12, por lo que dije “Menos 11 o 12” (que técnicamente es correcto, ya que era uno de esos). Gracias por señalar que es -11, cambiaré la respuesta para reflejar esto, gracias.

    – Preocupado binario

    3 de marzo de 2011 a las 7:40

  • En última instancia, es el complemento a 2 del número

    – niyasc

    13 de abril de 2015 a las 5:28

Pasar %d a printf le dice a printf que trate el argumento como un entero con signo, independientemente de lo que realmente pase. Utilice %u para imprimir como sin firmar.

  • Simple y comprensible, ¡la mejor respuesta!

    – Eugen Sunic

    30 de agosto de 2016 a las 11:33

  • Agradezco más esta respuesta. Llega al punto. Gracias.

    – rayryeng

    31 de enero de 2017 a las 16:43

Todo tiene que ver con la interpretación del valor.

Si asume enteros de 16 bits con y sin signo, aquí hay algunos ejemplos que no son exactamente correctos, pero demuestran el concepto.

0000 0000 0000 1100 int sin signo y valor int con signo 12

1000 0000 0000 1100 valor int con signo -12 y un entero grande sin signo.

Para enteros con signo, el bit de la izquierda es el bit de signo. 0 = positivo 1 = negativo

Para enteros sin signo, no hay bit de signo. el bit de la izquierda, le permite almacenar un número más grande en su lugar.

Entonces, la razón por la que no está viendo lo que espera es esa.

unsigned int x = -12, toma -12 como un número entero y lo almacena en x. x no tiene signo, por lo que lo que era un bit de signo ahora es una parte del valor.

printf le permite decirle al compilador cómo desea que se muestre un valor.

%d significa mostrarlo como si fuera un int firmado. %u significa mostrarlo como si fuera un int sin firmar.

c te permite hacer este tipo de cosas. Usted, el programador, tiene el control.

Algo así como un arma de fuego. es una herramienta Puede usarlo correctamente para hacer frente a ciertas situaciones, o incorrectamente para quitarse uno de los dedos del pie.

un caso posiblemente útil es el siguiente

unsigned int allBitsOn = -1;

Ese valor particular establece todos los bits en 1

1111 1111 1111 1111

que puede ser útil a veces.

avatar de usuario
linus kleen

printf('%d', x); 

Significa imprimir un entero con signo. Tendrás que escribir esto en su lugar:

printf('%u', x);

Además, todavía no imprimirá “12”, será “4294967284”.

avatar de usuario
Carreras de ligereza en órbita

Almacenan valores positivos. Pero está generando el valor positivo (muy alto) como un entero con signo, por lo que se reinterpreta nuevamente (de una manera definida por la implementación, podría agregar).

Usar la bandera de formato "%u en lugar de.

avatar de usuario
R.. GitHub DEJAR DE AYUDAR A ICE

Su programa tiene un comportamiento indefinido porque pasó el tipo incorrecto a printf (le dijiste que ibas a pasar un int pero pasaste un unsigned int). Considérese afortunado de que lo “más fácil” para la implementación fue simplemente imprimir silenciosamente el valor incorrecto y no saltar a un código que hace algo dañino…

  • pasar un entero sin signo donde se espera un argumento variable del tipo con signo correspondiente está realmente bien definido si el valor es representable en ambos tipos (ver C99 7.15.1.1 §2); sin embargo, este no es el caso aquí, pero cualquier implementación razonable del lenguaje C simplemente traducirá el valor a través de ‘juego de palabras’ (consulte la nota al pie 82 en la página 73)

    – Cristóbal

    3 de marzo de 2011 a las 12:13

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad