¿INT_MIN%-1 produce un comportamiento indefinido?
gcc overflow (5)
gcc genera código flotante que genera SIGFPE
para el siguiente código:
#include <limits.h>
int x = -1;
int main()
{
return INT_MIN % x;
}
Sin embargo, no puedo encontrar ninguna declaración en el estándar de que este código invoque un comportamiento indefinido o definido por la implementación. Por lo que puedo decir, se requiere devolver 0. ¿Es esto un error en gcc o me falta alguna excepción especial que hace el estándar?
El resultado de la operación de módulo con operandos negativos se deja definido en la implementación en C89, y se define en C99 por §6.5.5 / 6:
... el resultado del operador
/
es el cociente algebraico con cualquier parte fraccional descartada. 88) Si el cocientea/b
es representable, la expresión(a/b)*b
+a%b
será iguala
.88) Esto a menudo se llama "truncamiento hacia cero".
Para una representación de complemento de dos, INT_MIN / -1
es igual a INT_MAX + 1
, por lo que no es representable como un int
sin envoltura, y supongo que la implementación elige dejarlo explosivo.
La misma pregunta se hace aquí como un Informe de Defecto
http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#614
Lamentablemente, no veo que se indique explícitamente en la parte de la resolución que debería producir UB. La división efectivamente produciría UB, pero para el operador %
no es obvio.
Probablemente tengas razón de que esto puede considerarse un error en el estándar real. El borrador actual aborda este problema:
Si el cociente a / b es representable, la expresión (a / b) * b + a% b será igual a; de lo contrario, el comportamiento de a / by a% b no está definido.
Observando el código ensamblador generado por gcc (x se define como -1 antes en el ensamblaje):
movl x, %ecx
movl $-2147483648, %eax
movl %eax, %edx
sarl $31, %edx
idivl %ecx
La primera instrucción computacional, sarl
, desplaza a la derecha -2147483648
31 bits. Esto resulta en -1
que se pone en %edx
.
Siguiente idivl
se ejecuta. Esta es una operación firmada. Permítanme citar la descripción:
Divide el contenido de la doble palabra contenida en los registros combinados% edx:% eax por el valor en la ubicación de registro o memoria especificada.
Entonces -1:-2147483648 / -1
es la división que sucede. -1:-2147483648
interpretada como una palabra doble es igual a -2147483648
(en una máquina complementaria de dos). Ahora ocurre -2147483648 / -1
que devuelve 2147483648
. ¡AUGE! Esa es una más que INT_MAX
.
Acerca de la pregunta ¿por qué? ¿Esto es un error en gcc o me falta alguna excepción especial que hace el estándar?
En el estándar C99 esto es UB implícito (§6.5.5 / 6):
... el resultado del operador / es el cociente algebraico con cualquier parte fraccional descartada.88) Si el cociente a / b es representable, la expresión (a / b) * b + a% b será igual a.
INT_MIN / -1
no se puede representar, por lo tanto, esto es UB.
En C89, sin embargo, el operador% está definido en la implementación y si se trata de un error del compilador o no se puede debatir. Sin embargo, el problema aparece en gcc: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=30484