2011-01-05 14 views
34

Por favor, considere el siguiente código y comentarios:La inconsistencia en el comportamiento de división por cero entre diferentes tipos de valor

Console.WriteLine(1/0); // will not compile, error: Division by constant zero 

int i = 0; 
Console.WriteLine(1/i); // compiles, runs, throws: DivideByZeroException 

double d = 0; 
Console.WriteLine(1/d); // compiles, runs, results in: Infinity 

Puedo entender el compilador comprobar activamente la división por cero constante y la DivideByZeroException en tiempo de ejecución, pero:

¿Por qué usar un doble en un retorno de división por cero Infinity en lugar de arrojar una excepción? ¿Es esto por diseño o es un error?

Sólo por diversión, lo hice en VB.NET, así, con resultados más consistentes "":

dim d as double = 0.0 
Console.WriteLine(1/d) ' compiles, runs, results in: Infinity 

dim i as Integer = 0 
Console.WriteLine(1/i) ' compiles, runs, results in: Infinity 

Console.WriteLine(1/0) ' compiles, runs, results in: Infinity 

EDIT:

basado en la retroalimentación de kekekela me encontré con lo siguiente, que dio lugar a en el infinito:

Console.WriteLine(1/
    .0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001); 

Esta prueba parece corroborar la idea y un doble literal de 0.0 es en realidad una fracción muy, muy pequeña que dará como resultado Infinito ...

+3

Aquí está mi artículo sobre el tema: http://blogs.msdn.com/b/ericlippert/archive/2009/10/15/as-timeless-as-infinity.aspx –

+0

@EricLippert ¡genial! – Jalal

Respuesta

32

En pocas palabras: el tipo double define un valor para el infinito mientras que el tipo int no lo hace. Por lo tanto, en el caso double, el resultado del cálculo es un valor que se puede expresar en el tipo dado desde que se definió. En el caso int, no hay ningún valor para el infinito y, por lo tanto, no hay manera de devolver un resultado preciso. De ahí la excepción.

VB.NET hace las cosas un poco diferente; la división entera da como resultado automáticamente un valor de coma flotante usando el operador /. Esto es para permitir a los desarrolladores escribir, por ejemplo, la expresión 1/2, y hacer que evalúe a 0.5, lo que algunos considerarían intuitivo. Si desea ver un comportamiento coherente con C#, intente esto:

Console.WriteLine(1 \ 0) 

Nota el uso de la número entero división operador (\, no /) anterior. Creo que obtendrás una excepción (o un error de compilación, no estoy seguro de cuál).

Del mismo modo, intente esto:

Dim x As Object = 1/0 
Console.WriteLine(x.GetType()) 

El código anterior producirá la salida System.Double.

En cuanto al punto acerca de la imprecisión, aquí hay otra forma de verlo. No es que el tipo double no tenga ningún valor para exactamente cero (lo hace); más bien, el tipo double no está destinado a proporcionar resultados matemáticamente exactos en primer lugar. (Ciertos valores pueden representarse exactamente, sí. Pero los cálculos no prometen precisión.) Después de todo, el valor de matemático expresión 1/0 no está definido (última vez que lo verifiqué). Pero 1/x se acerca al infinito cuando x se aproxima a cero. Por lo tanto, desde esta perspectiva, si no podemos representar la mayoría de las fracciones n/mexactamente de todos modos, tiene sentido tratar el caso x/0 como aproximado y dar el valor se acerca a --al menos, infinito se define, al menos.

+1

Aunque es muy útil, esta respuesta también es un poco incorrecta. La respuesta es incorrecta porque la división por cero no es infinito; está matemáticamente indefinido. La respuesta real es que los dobles NO son números reales (R) como se señala en la última parte de esta respuesta. Son números flotantes, y OP está tratando de aplicar el razonamiento de números reales en algo que no es un número real. Parecen similares la mayor parte del tiempo porque fueron diseñados para ser similares, pero son fundamentalmente diferentes. Los dobles definen algo llamado 'NaN'," No es un número ", (continúa ...) – AnorZaken

+1

(cont ...) que sería un" resultado "más matemáticamente correcto si esto fuera de hecho números reales. Sin embargo, no devolvió NaN. La razón es más o menos como se describe en la respuesta: porque en la lógica de punto flotante se supone que "0.0" es un número muy pequeño. No obstante, no puede decir que es un número pequeño, indicando que existe y la representación exacta de cero es engañosa, porque no hay una correspondencia de 1 a 1 de float a R. Más bien, cada valor flotante se asigna a un rango de real -values, y el flotante "0.0" incluye tanto _actual_ zero _and_ un rango de otros valores pequeños. – AnorZaken

+0

Un ejemplo más de que el punto flotante es fundamentalmente diferente de los números reales es que los números flotantes definen "-0.0" (cero negativo), y OP dividido por ese resultado habría sido 'Infinito negativo'. Sin embargo, ¿a qué crees que evalúa '0.0 == -0.0'? – AnorZaken

7

Un doble es un número de coma flotante y no un valor exacto, entonces lo que realmente está dividiendo desde el punto de vista del compilador es algo cercano a cero, pero no exactamente cero.

+1

En realidad, los dobles tienen una representación de un valor que es exactamente cero. La verdadera razón es que, por definición, una división por cero de un doble da como resultado el valor de Inf, es decir, no por accidente. – slebetman

+2

@slebetman - Si los dobles tienen "una representación de un valor que es exactamente cero", ¿por qué los dobles distinguen entre "+0" y "-0" y por qué 1/+ 0 y 1/-0 dan resultados diferentes? La idea de un "cero firmado" solo tiene sentido si esos valores se consideran valores positivos o negativos que son demasiado pequeños para representarse normalmente. Tenga en cuenta que no hay cero sin signo en los tipos de coma flotante IEEE 754. –

+1

@Jeffrey: +0 y -0 siguen siendo 0. Incluso si argumenta que los números que son más pequeños que épsilon están representados por 0, todavía está diciendo que los números que son demasiado pequeños son efectivamente 0. Sí, en matemáticas -0 no sentido pero cuando se diseñó 754, los físicos que ejecutan simulaciones argumentaron que querían saber de qué dirección provenía un resultado si el límite de un cálculo llevaba a 0. – slebetman

1

Esto probablemente tiene algo que ver con el hecho de que los números de coma flotante de doble punto y coma flotante estándar IEEE tienen un valor de "infinito" especificado. .NET solo está exponiendo algo que ya existe, a nivel de hardware.

Consulte la respuesta de kekekela para saber por qué esto tiene sentido, lógicamente.

1

Esto es por diseño porque el tipo double cumple con IEEE 754, el estándar para la aritmética de coma flotante. Consulte la documentación para Double.NegativeInfinity y Double.PositiveInfinity.

El valor de esta constante es el resultado de dividir un número positivo {o negativo} por cero.

2

Porque el punto flotante "numérico" no es nada por el estilo. operaciones de punto flotante:

  • no son asociativos
  • no son distributiva
  • pueden no tener un inverso multiplicativo

(ver http://www.cs.uiuc.edu/class/fa07/cs498mjg/notes/floating-point.pdf para algunos ejemplos)

El punto flotante es una construir para resolver un problema específico, y se usa cuando no debería. Creo que son bastante horribles, pero eso es subjetivo.

Cuestiones relacionadas