and c# checked unchecked overflowexception

c# - and - ¿Por qué dividir int.MinValue por-1 arrojó OverflowException en un contexto no verificado?



checked and unchecked (3)

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, se aplica la resolución de sobrecarga del operador binario (§7.3.4) para seleccionar una implementación de operador específica. Los operandos se convierten a los tipos de parámetros del operador seleccionado, y el tipo de resultado es el tipo de retorno del operador. Los operadores de división predefinidos se enumeran a continuación. Todos los operadores calculan el cociente de x y y.

  • División entera:
    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 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 que el valor absoluto del cociente de los dos operandos. El resultado es cero o positivo cuando los dos operandos tienen el mismo signo y cero o negativo cuando los dos operandos tienen signos opuestos. Si el operando izquierdo es el valor int o largo representable más pequeño y el operando derecho es -1 , se produce un desbordamiento. En un contexto verificado, esto provoca que se System.ArithmeticException (o una subclase del mismo). En un contexto sin marcar, se define la implementación en cuanto a si se lanza System.ArithmeticException (o una subclase de la misma) o si el desbordamiento no se informa con el valor resultante que es el del operando izquierdo.

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

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

Por ejemplo:

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

no causa una excepción


Esta no es una excepción sobre la que el compilador de C # o el jitter tienen control. Es específico de los procesadores Intel / AMD, la CPU genera una trampa #DE (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. CLR lo convierte diligentemente en una excepción administrada coincidente.

El Manual del procesador Intel no es exactamente una mina de oro de información al respecto:

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

En inglés: el resultado de la división con signo es +2147483648, no representable en un int ya que es Int32.MaxValue + 1. De lo contrario, un efecto colateral inevitable de la forma en que el procesador representa los valores negativos, usa una codificación de dos complementos. Que produce un único 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. Mismo tipo de desbordamiento que -Int32.MinValue , excepto que el procesador no atrapa en la instrucción NEG y solo produce un resultado de 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 de implementación (capítulo 7.8.2) al observar el comportamiento especial. No hay otra cosa razonable que puedan hacer con eso, generar el código para manejar la excepción seguramente se consideró demasiado poco práctico, produciendo un código lento e indescifrable. No es el modo C #.

El lenguaje C y C ++ aumenta la apuesta al convertirlo en un comportamiento indefinido. Eso realmente puede ponerse feo, como un programa compilado con el compilador gcc o g ++, generalmente 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 bloquea, quemando 100% de núcleo con el procesador generando constantemente #DE trampas. Convertir la división en la legendaria instrucción Halt and Catch Fire :)


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 largo representable más pequeño y el operando derecho es -1, se produce un desbordamiento. En un contexto verificado, [...]. En un contexto sin marcar, se define la implementación en cuanto a si se lanza System.ArithmeticException (o una subclase de la misma) o si el desbordamiento no se informa con el valor resultante que es el del operando izquierdo.

Por lo tanto, el hecho de que esto arroje una excepción en un contexto no verificado no es, de hecho, un error, ya que el comportamiento está definido por la implementación.