Por qué dividir int.MinValue por -1 lanzó OverflowException en contexto sin marcar?


int y = -2147483648;
int z = unchecked(y / -1);

La segunda línea causa un OverflowException. No debería unchecked prevenir esto?

Por ejemplo:

int y = -2147483648;
int z = unchecked(y * 2);

No causa una excepción.

Author: BartoszKP, 2014-10-27

3 answers

Esta no es una excepción sobre la que el compilador de C# o el jitter tengan algún control. Es específico de los procesadores Intel/AMD, la CPU genera un # DE trap (Error de división) cuando falla la instrucción IDIV. El sistema operativo maneja la trampa del procesador y la refleja de nuevo en el proceso con una excepción STATUS_INTEGER_OVERFLOW. El CLR lo traduce obedientemente a una excepción administrada coincidente.

El Manual del Procesador Intel no es exactamente una mina de oro de información sobre it:

Los resultados no integrales se truncan (trocean) hacia 0. El resto es siempre menor que el divisor en magnitud. El desbordamiento se indica con la excepción #DE (error de división) en lugar de con la bandera CF.

En inglés: el resultado de la división firmada es +2147483648, no representable en un int ya que es Int32.MaxValue + 1. De lo contrario, un efecto secundario inevitable de la forma en que el procesador representa los valores negativos, utiliza el complemento de dos codificación. Que produce un solo valor para representar 0, dejando un número impar de otras codificaciones posibles para representar valores negativos y positivos. Hay uno más para los valores negativos. El mismo tipo de desbordamiento que -Int32.MinValue, excepto que el procesador no atrapa la instrucción NEG y solo produce un resultado basura.

El lenguaje C# no es, por supuesto, el único con este problema. La especificación del lenguaje C # hace que sea un comportamiento definido por la implementación (capítulo 7.8.2) comportamiento. Ninguna otra cosa razonable que pudieran hacer con él, generar el código para manejar la excepción seguramente se consideró demasiado poco práctico, produciendo un código increíblemente lento. No en C#.

Las especificaciones del lenguaje C y C++ suben la apuesta haciendo que sea un comportamiento indefinido. Eso puede ponerse realmente feo, como un programa compilado con el compilador gcc o g++, típicamente con la cadena de herramientas MinGW. Que tiene soporte de tiempo de ejecución imperfecto para SEH, se traga la excepción y permite que el procesador reinicie la instrucción de división. El programa se cuelga, quemando 100% core con el procesador generando constantemente # DE traps. Convirtiendo la división en el legendario Detener y Atrapar instrucciones de fuego :)

 31
Author: Hans Passant,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-10-28 11:50:26

La sección 7.72 (Operador de división) de las especificaciones de C# 4 establece:

Si el operando izquierdo es el valor int o long representable más pequeño y el operando derecho es -1, se produce un desbordamiento. En un contexto comprobado, [...]. En un contexto sin marcar, se define la implementación en cuanto a si un Sistema.ArithmeticException (o una subclase de la misma) es lanzada o el desbordamiento no se reporta con el valor resultante siendo el del operando izquierdo.

Así que el hecho de que esto arroja una excepción en un contexto sin marcar no es de hecho un error, ya que el comportamiento está definido por la implementación.

 33
Author: Servy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-10-28 13:48:32

De acuerdo con la sección 7.8.2 de la Especificación del Lenguaje C # 5.0 tenemos el siguiente caso:

7.8.2 operador de División
Para una operación de la forma x / y, sobrecarga del operador binario la resolución (§7.3.4) se aplica para seleccionar un operador específico aplicación. Los operandos se convierten a los tipos de parámetros de el operador seleccionado, y el tipo del resultado es el tipo devuelto del operador. Los operadores de división predefinidos son listed below. Todos los operadores calculan el cociente de x e y.

  • división de Enteros:
    int operator /(int x, int y);
    uint operator /(uint x, uint y);
    long operator /(long x, long y);
    ulong operator /(ulong x, ulong y);
    Si el valor del operando derecho es cero, se lanza un System.DivideByZeroException. La división redondea el resultado hacia cero. Por lo tanto, el valor absoluto del resultado es el entero más grande posible que es menor o igual al valor absoluto del cociente de los dos operandos. El resultado es cero o positivo cuando los dos operandos tienen la el mismo signo y cero o negativo cuando los dos operandos tienen signos opuestos. Si el operando izquierdo es el valor int o long representable más pequeño y el operando derecho es -1, se produce un desbordamiento. En un contexto comprobado, esto causa que se lance un System.ArithmeticException (o una subclase del mismo). En un contexto no controlado, se define como implementación si se lanza una System.ArithmeticException (o una subclase de la misma) o el desbordamiento no se informa con el valor resultante siendo el de la izquierda operando.
 10
Author: Markus Safar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-10-27 19:22:10