tabla suma signos resta regla positivos positivo operaciones numeros negativos matematicos mas los ley enteros ejercicios con c standards unsigned integer-arithmetic

suma - ¿Es el comportamiento definido de la resta entera sin signo?



signos matematicos (4)

Me encontré con el código de alguien que parece creer que hay un problema al sustraer un entero sin signo de otro entero del mismo tipo cuando el resultado sería negativo. Entonces, ese código sería incorrecto incluso si funciona en la mayoría de las arquitecturas.

unsigned int To, Tf; To = getcounter(); while (1) { Tf = getcounter(); if ((Tf-To) >= TIME_LIMIT) { break; } }

Esta es la única cita vagamente relevante del estándar C que pude encontrar.

Un cómputo que involucre operandos sin firmar nunca puede sobrevolverse, porque un resultado que no puede ser representado por el tipo de entero sin signo resultante es un módulo reducido que es uno mayor que el valor más grande que puede ser representado por el tipo resultante.

Supongo que se podría tomar esa cita para indicar que cuando el operando correcto es más grande, la operación se ajusta para que tenga sentido en el contexto de los números truncados en el módulo.

es decir

0x0000 - 0x0001 == 0x 1 0000 - 0x0001 == 0xFFFF

en lugar de utilizar la semántica firmada dependiente de la implementación:

0x0000 - 0x0001 == (sin signo) (0 + -1) == (0xFFFF pero también 0xFFFE o 0x8001)

¿Cuál o qué interpretación es la correcta? ¿Está definido en absoluto?


Bueno, la primera interpretación es correcta. Sin embargo, su razonamiento sobre la "semántica firmada" en este contexto es incorrecto.

De nuevo, tu primera interpretación es correcta. La aritmética sin signo sigue las reglas de la aritmética del módulo, lo que significa que 0x0000 - 0x0001 evalúa como 0xFFFF para los tipos sin signo de 32 bits.

Sin embargo, la segunda interpretación (la basada en "semántica firmada") también es necesaria para producir el mismo resultado. Es decir, incluso si evalúa 0 - 1 en el dominio de tipo con signo y obtiene -1 como el resultado intermedio, este -1 aún se requiere para producir 0xFFFF cuando más tarde se convierte en tipo sin signo. Incluso si alguna plataforma utiliza una representación exótica para enteros con signo (complemento de 1, magnitud con signo), esta plataforma aún debe aplicar reglas de aritmética de módulo al convertir valores enteros con signo a los sin signo.

Por ejemplo, esta evaluación

signed int a = 0, b = 1; unsigned int c = a - b;

todavía está garantizado para producir UINT_MAX en c , incluso si la plataforma está utilizando una representación exótica para enteros con signo.


Con números sin signo de tipo unsigned int o más grande, en ausencia de conversiones de tipo, ab se define como ceder el número sin signo que, cuando se agrega a b , producirá a . La conversión de un número negativo a sin signo se define como la obtención del número que, cuando se agrega al número original de signo invertido, arrojará cero (por lo que convertir -5 en no firmado arrojará un valor que, cuando se agrega a 5, arrojará cero) .

Tenga en cuenta que los números sin signo más pequeños que unsigned int pueden promocionarse para escribir int antes de la resta, el comportamiento de ab dependerá del tamaño de int .


Cuando trabaja con tipos sin signo , se está llevando a cabo una aritmética modular (también conocida como comportamiento de "envoltura" ). Para entender esta aritmética modular , solo eche un vistazo a estos relojes:

9 + 4 = 1 ( 13 mod 12 ), por lo que en la otra dirección es: 1 - 4 = 9 ( -3 mod 12 ). El mismo principio se aplica al trabajar con tipos sin firmar. Si el tipo de resultado unsigned está unsigned , se lleva a cabo una aritmética modular.

Ahora observe las siguientes operaciones almacenando el resultado como un unsigned int :

unsigned int five = 5, seven = 7; unsigned int a = five - seven; // a = (-2 % 2^32) = 4294967294 int one = 1, six = 6; unsigned int b = one - six; // b = (-5 % 2^32) = 4294967291

Cuando desee asegurarse de que el resultado esté signed , entonces lo almacenó en una variable con signed o lo lanzó a signed . Cuando desee obtener la diferencia entre los números y asegurarse de que la aritmética modular no se aplicará, entonces debería considerar usar la función abs() definida en stdlib.h :

int c = five - seven; // c = -2 int d = abs(five - seven); // d = 2

Tenga mucho cuidado, especialmente al escribir las condiciones, porque:

if (abs(five - seven) < seven) // = if (2 < 7) // ... if (five - seven < -1) // = if (-2 < -1) // ... if (one - six < 1) // = if (-5 < 1) // ... if ((int)(five - seven) < 1) // = if (-2 < 1) // ...

pero

if (five - seven < 1) // = if ((unsigned int)-2 < 1) = if (4294967294 < 1) // ... if (one - six < five) // = if ((unsigned int)-5 < 5) = if (4294967291 < 5) // ...


El resultado de una resta que genera un número negativo en un tipo sin signo está bien definido:

  1. [...] Un cálculo que involucre operandos sin firmar nunca puede desbordarse, porque un resultado que no puede ser representado por el tipo de entero sin signo resultante es módulo reducido el número que es uno mayor que el valor más grande que puede ser representado por el tipo resultante. (ISO / IEC 9899: 1999 (E) §6.2.5 / 9)

Como puede ver, (unsigned)0 - (unsigned)1 es igual a -1 módulo UINT_MAX + 1, o en otras palabras, UINT_MAX.

Tenga en cuenta que, si bien dice "Un cómputo que involucre operandos sin firmar nunca puede desbordarse", lo que podría llevarlo a creer que se aplica solo por exceder el límite superior, esto se presenta como una motivación para la parte vinculante real de la oración: "a el resultado que no puede ser representado por el tipo de entero sin signo resultante es un módulo reducido, el número que es uno mayor que el valor más grande que puede ser representado por el tipo resultante ". Esta frase no está restringida al desbordamiento del límite superior del tipo, y se aplica igualmente a valores demasiado bajos para ser representados.