2010-04-14 9 views
21

En el código de ejemplo siguiente, estoy dividiendo por cero, que cuando lo paso con el depurador el (dividendo/divisor) produce un Infinito o NaN (si el divisor es cero). Cuando selecciono este resultado, obtengo un resultado válido, generalmente algo así como -9223372036854775808. ¿Por qué es este elenco válido? ¿Por qué no deja de ejecutarse (arroje una excepción, por ejemplo) en lugar de asignar un valor arbitrario?¿Por qué lanzar un NaN a un rendimiento largo es un resultado válido?

double divisor = 0; 
double dividend = 7; 
long result = (long)(dividend/divisor); 
+0

¿Ha intentado ver el 'MSIL' generado por ese código? –

+7

Paginación Eric Lippert ... Eric Lippert por favor acércate a la recepción. –

+0

En C#, ¿ese tipo de yeso solo tiene un efecto como lo haría en C, que es solo determinar cómo debe interpretarse el almacenamiento en bruto? – Pointy

Respuesta

24

¿Por qué es válido este elenco?

Un reparto es válido si se conoce en tiempo de compilación que la conversión podría tener éxito o siempre tiene éxito. Los lanzamientos solo son ilegales cuando la conversión no puede tener éxito. (Por ejemplo, enviar un tipo sellado a una interfaz que no implementa). Una conversión de doble a larga podría tener éxito. Por lo tanto, el elenco es válido.

¿Por qué no deja de ejecutarse (arroje una excepción, por ejemplo) en lugar de asignar un valor arbitrario?

¡Porque no pidió una excepción!La especificación es extremadamente clara sobre cuál es el comportamiento esperado. Véase la sección 6.2.1:

Para una conversión de flotador o doble a un tipo integral, el procesamiento depende del desbordamiento de la comprobación contexto en el que la conversión se lleva a cabo:

En un contexto comprobado, la conversión procede de la siguiente manera:

• Si el valor del operando es NaN o infinito, se lanza una System.OverflowException.

[...]

En un contexto no se controla, la conversión siempre tiene éxito, y se procede de la siguiente manera.

• Si el valor del operando es NaN o infinito, el resultado de la conversión es un valor no especificado del tipo de destino.

Está ejecutando el código en un contexto sin marcar; no has pedido ninguna excepción, así que no obtienes ninguna excepción. Si quieres una excepción, pide una; usa un contexto verificado

3

Depende del tipo de datos. Los diferentes tipos de datos tendrán diferentes resultados para dividir por cero.

http://blogs.msdn.com/rafats/archive/2006/07/20/673337.aspx

y

http://www.eggheadcafe.com/software/aspnet/30920566/divide-by-zero-question.aspx

De Jon Skeet en un sitio diferente (el siguiente enlace):

En una palabra, los estándares. Al menos, sospecho que esa es la razón. flotante/doble, siga las reglas estándar de IEC 60559 para la aritmética, , que incluye la división por cero, lo que da como resultado un valor "infinito" en lugar de arrojando una excepción.

decimal no (o al menos no tener a - la especificación C# permite la posibilidad) apoyar un "infinito" valor mientras flotador/doble do. Similar decimal no tiene un NaN especificado.

división por cero explicación: http://www.philforhumanity.com/How_to_Divide_by_Zero.html

+1

Entiendo los resultados de la división, pero lo que no entiendo es cómo se puede convertir un resultado de división no válido (es decir, NaN) en un resultado válido (en este caso, un largo). – brainimus

+0

Porque técnicamente dividir por cero es matemáticamente legal. Simplemente no está representado por un número. Implementaron dividir por cero en aritmética de punto flotante de una manera que (más o menos) refleja esto. – kemiller2002

13

Por defecto, C# es la aritmética operaciones sin control, por lo que no válidas no lanzar excepciones.

Se puede utilizar un checked block para forzar el tiempo de ejecución para comprobar si hay desbordamiento y lanzar excepciones, como esto:

checked { 
    double divisor = 0; 
    double dividend = 7; 

    long result = (long)(dividend/divisor); 
} 

Tenga en cuenta que habrá una ligera pérdida de rendimiento.

4

También puede utilizar el indicador "marcado" al compilar, que funcionará de la misma manera que envolver todo su código en un bloque marcado.

11

El comportamiento se documenta explícitamente en la especificación del lenguaje C#, sección 6.2.1:

Para una conversión de flotador o doble a un tipo integral, el procesamiento depende del contexto de desbordamiento de control (§7.5 0,12) en la que la conversión tiene lugar:

en un contexto comprobado, el producto de conversión como sigue:

  • Si el valor de t El operando es NaN o infinito, se emite un System.OverflowException.
  • De lo contrario, el operando fuente se redondea hacia cero al valor integral más cercano. Si este valor integral está dentro del rango de el tipo de destino, este valor es el resultado de la conversión.
  • De lo contrario, se lanza una System.OverflowException.

En un contexto no verificado, la conversión siempre se realiza correctamente, y continúa de la siguiente manera.

  • Si el valor del operando es NaN o infinito, el resultado de la conversión es un valor no especificado de tipo de destino.
  • De lo contrario, el operando fuente se redondea hacia cero al valor integral más cercano. Si este valor integral está dentro del rango de el tipo de destino, este valor es el resultado de la conversión.
  • De lo contrario, el resultado de la conversión es un valor no especificado de el tipo de destino.

Negrita se ha añadido énfasis. Tienes infinito y un contexto sin marcar. El valor que obtienes no está especificado. Use la palabra clave verificada para que sea una bomba.

Cuestiones relacionadas