La expresión int.Minvalue / -1
da como resultado un comportamiento definido por la implementación de acuerdo con la especificación de C#:
7.8.2 Operador de división
Si el operando de la izquierda es el valor entero o largo más pequeño representable y el operando de la derecha es –1, se produce un desbordamiento. En un contexto comprobado, esto provoca que se lance una System.ArithmeticException (o una subclase de la misma). En un contexto no verificado, está definido por la implementación si se lanza una System.ArithmeticException (o una subclase de la misma) o si el desbordamiento no se informa y el valor resultante es el del operando izquierdo.
Programa de prueba:
var x = int.MinValue; var y = -1; Console.WriteLine(unchecked(x / y));
Esto genera una OverflowException
en .NET 4.5 de 32 bits, pero no es necesario.
¿Por qué la especificación deja la implementación del resultado definida? Aquí está el caso en contra de hacer eso:
idiv
siempre da como resultado una excepción en este caso. También es interesante el hecho de que si x / y
es una constante de tiempo de compilación, de hecho no se unchecked(int.MinValue / -1) == int.MinValue
:
Console.WriteLine(unchecked(int.MinValue / -1)); //-2147483648
Esto significa que x / y
puede tener diferentes comportamientos dependiendo de la forma sintáctica que se use (y no solo dependiendo de los valores de x
e y
). Esto está permitido por la especificación, pero parece una elección imprudente. ¿Por qué C# se diseñó así?
Una pregunta similar señala en qué parte de la especificación se prescribe este comportamiento exacto, pero no responde (suficientemente) por qué el lenguaje se diseñó de esta manera. Las opciones alternativas no se discuten.
Este es un efecto secundario del hermano mayor de C# Language Specification, Ecma-335 , la especificación Common Language Infrastructure. La Sección III, capítulo 3.31 describe lo que hace el código de operación DIV. Una especificación a la que la especificación C# a menudo tiene que ceder, bastante inevitable. Especifica que puede lanzar pero no lo exige.
De lo contrario, una evaluación realista de lo que hacen los procesadores reales. Y el que todo el mundo usa es el raro. Los procesadores Intel son excesivamente peculiares en cuanto al comportamiento de desbordamiento, fueron diseñados en la década de 1970 con la suposición de que todo el mundo usaría la instrucción INTO. Nadie lo hace, una historia para otro día. Sin embargo, no ignora el desbordamiento en un IDIV y levanta la trampa #DE, no puede ignorar ese fuerte estallido.
Bastante difícil escribir una especificación de idioma además de una especificación de tiempo de ejecución lanosa además de un comportamiento inconsistente del procesador. Poco podía hacer el equipo de C# con eso más que reenviar el lenguaje impreciso. Ya fueron más allá de la especificación al documentar OverflowException en lugar de ArithmeticException. Muy travieso. Tuvieron un vistazo.
Un vistazo que reveló la práctica. Es muy poco probable que sea un problema, el jitter decide si se debe o no en línea. Y la versión no en línea arroja, la expectativa es que la versión en línea también lo haga. Nadie ha sido decepcionado todavía.
Se dice que uno de los principales objetivos de diseño de C# es la "Ley de sorpresa mínima". De acuerdo con esta guía, el compilador no debe intentar adivinar la intención del programador, sino que debe indicarle al programador que se necesita orientación adicional para especificar correctamente la intención. Esto se aplica al caso de interés porque, dentro de las limitaciones de la aritmética del complemento a dos , la operación genera un resultado muy sorprendente: Int32.MinValue / -1 se evalúa como Int32.MinValue . Se produjo un desbordamiento y se requeriría un bit 33 no disponible, de 0, para representar correctamente el valor correcto de Int32.MaxValue + 1 .
Como se esperaba, y se señaló en su cita, en un contexto verificado, se genera una Excepción para alertar al programador sobre la falla al especificar correctamente la intención. En un contexto no verificado, la implementación puede comportarse como en el contexto verificado o permitir el desbordamiento y devolver el resultado sorprendente. Hay ciertos contextos, como el cambio de bits, en los que es conveniente trabajar con int firmados pero donde el comportamiento de desbordamiento es realmente esperado y deseado. Al consultar las notas de implementación, el programador puede determinar si este comportamiento es realmente el esperado .