2011-05-08 18 views
23

gcc genera código que plantea SIGFPE el siguiente código flotante:¿INT_MIN% -1 produce un comportamiento indefinido?

#include <limits.h> 
int x = -1; 
int main() 
{ 
    return INT_MIN % x; 
} 

Sin embargo no puedo encontrar ninguna declaración en la norma que este código invoca 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?

+2

Tenga en cuenta que la variable global es evitar que gcc desde la optimización de la computación. –

+1

¿Qué esperarías como resultado tratando de obtener el módulo de un número negativo? – joce

+0

"Cuando a o n son negativos, esta definición ingenua se rompe y los lenguajes de programación difieren en cómo se definen estos valores". http://en.wikipedia.org/wiki/Modulo_operation – joce

Respuesta

15

probablemente tiene usted razón de que esto puede ser considerado como un error en el estándar real. El current draft direcciones este problema:

Si el cociente a/b es representable, la expresión (a/b) * b + a% b se igual a a; de lo contrario, el comportamiento de tanto a/by a% b no está definido.

+0

Aceptado; esta respuesta aborda el error y la solución adoptada por el comité. Lo siento, tomó tanto tiempo. –

+0

¿Es probable que este lenguaje esté incluido en un TC a C99? – caf

+0

@caf, supongo que no habrá TC a C99, porque C1x está destinado a reemplazarlo. –

14

Mirando el código ensamblador generado por gcc (x se define como -1 anteriormente en el montaje):

movl x, %ecx 
movl $-2147483648, %eax 
movl %eax, %edx 
sarl $31, %edx 
idivl %ecx 

La primera instrucción computacional, sarl, a la derecha se desplaza -2147483648 31 bits. Esto da como resultado -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 palabra doble contenida en los combinados% EDX: EAX registros% por el valor en el Registro de Localización o la memoria especificada.

Así que -1:-2147483648/-1 es la división que sucede. -1:-2147483648 interpretado como una palabra doble es igual a -2147483648 (en una máquina con dos complementos). Ahora -2147483648/-1 sucede que devuelve 2147483648. ¡AUGE! Eso es uno más que INT_MAX.


Sobre 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ícita (§6.5.5/6):

... el resultado de la/operador es el cociente algebraico con cualquier parte discarded.88 fraccional) Si el cociente una/b es representable, la expresión (a/b) * b + a% b será igual a.

INT_MIN/-1 no se pueden representar, por lo tanto esto es UB.

En C89, sin embargo, el operador% es la implementación definida y si se trata de un error del compilador o no se puede debatir. El problema se enumera en gcc sin embargo: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=30484

+2

Su información sobre * cómo * está sucediendo el 'SIGFPE' es correcta. Sin embargo, mi preocupación es si esto es correcto. Nunca pedí el compilador para calcular 'INT_MIN/-1' (lo que resultaría claramente en la UB ya que se desborda), sólo para calcular' INT_MIN% -1', y no puedo encontrar ninguna declaración en la norma de que éste invoca UB. Aún así, una respuesta informativa. –

+0

Con respecto a su cita de 6.5.5/6: en el párrafo anterior (5), se lee "el resultado del operador% es el resto". El párrafo 6 aclara que cuando "el resto" podría ser ambiguo, pero "el resto" no es ambiguo cuando "a" es un múltiplo entero de "b"; en este caso el resto único es 0. –

+5

@R .: Hmm, espera un segundo, los estados estándar "Si el cociente a/b es representable", que no es el caso en INT_MIN% -1. No indica qué sucederá cuando esto sucede, por lo tanto, es UB. – orlp

9
+0

Gracias por el enlace informativo. Al menos parece que la mayoría de la gente está de acuerdo en que es un error, pero el enfoque de "dejarlo roto por defecto y requerir una opción especial para arreglarlo" suena ridículo ... –

+0

¿Hay alguna expresión aritmética/bit a bit simple que evalúe 'x' si' x! = - 1' y '1' si' x == 1'? Si es así, el compilador podría aplicar esto al denominador antes de ejecutar '%' con un denominador de variable cuando '/' no se esté ejecutando también con el mismo denominador. –

+0

@R ..: "desactivado por defecto pero activado en modos conformes con el estándar" suena bien, sin embargo, y eso es lo que se sugiere en el error. 'gcc' no es una implementación conforme, ni pretende serlo. 'gcc -std = c99' tampoco se ajusta por completo, pero intenta ser ... –

3

El resultado de la operación de módulo con operandos negativos queda definido por la implementación en C89, y se definen en C99 por §6.5.5/6:

y hellip; el resultado del operador / es la algebraica cociente con cualquier parte fraccional descartada. 88) Si el cociente a/b es representable, la expresión (a/b)*b + a%b será igual a a.

88) Esto a menudo se denomina "truncamiento hacia cero".

Para una representación en complemento a 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. Se le pide

+0

Sin embargo, el operador'/'no aparece en mi programa. Y independientemente de cómo defina el resultado con operandos negativos ("redondeo" hacia 0 o hacia abajo), -1 divide de manera uniforme cualquier cosa y, por lo tanto, el único resto posible es cero, matemáticamente. –

+0

@R ..: Estaba citando una referencia para el comportamiento con respecto a los operandos negativos. El cálculo falla a medida que GCC lo implementa, por lo que es básicamente un error, si necesita que lo diga en muchas palabras. –

+3

@R ..: para decirlo de otra manera, '%' se define solo cuando '/' está definido, y como '/' está aquí indefinido, '%' también está indefinido, aunque probablemente no debería estarlo. –

Cuestiones relacionadas