language agnostic - online - ¿Por qué la división por cero en el estándar IEEE754 da como resultado un valor infinito?
punto flotante ieee 754 online (4)
Es una tontería desde la perspectiva matemática.
Sí. No. Más o menos.
Lo que pasa es que: los números de coma flotante son aproximaciones. Desea utilizar una amplia gama de exponentes y un número limitado de dígitos y obtener resultados que no son completamente incorrectos. :)
La idea detrás de IEEE-754 es que cada operación podría desencadenar "trampas" que indican posibles problemas. Son
- Ilegal (operación sin sentido como sqrt de número negativo)
- Desbordamiento (demasiado grande)
- Underflow (demasiado pequeño)
- División por cero (Lo que no te gusta)
- Inexact (Esta operación puede dar resultados incorrectos porque está perdiendo precisión)
Ahora muchas personas, como científicos e ingenieros, no quieren que les moleste escribir rutinas trampa. Así que Kahan, el inventor de IEEE-754, decidió que cada operación también debería devolver un valor predeterminado razonable si no existen rutinas trampa.
Son
- NaN por valores ilegales
- infinitos firmados para Overflow
- ceros firmados para Underflow
- NaN para resultados indeterminados (0/0) e infinitos para (x / 0 x! = 0)
- resultado de operación normal para Inexact
El hecho es que en el 99% de los casos, los ceros son causados por subdesbordamiento y, por lo tanto, en el 99% de todos los casos Infinito es "correcto", incluso si está mal desde una perspectiva matemática.
Solo tengo curiosidad, ¿por qué en IEEE-754
cualquier número de flotación no cero dividido por cero da como resultado un valor infinito? Es una tontería desde la perspectiva matemática. Entonces, creo que el resultado correcto para esta operación es NaN.
La función f (x) = 1 / x no se define cuando x = 0, si x es un número real. Por ejemplo, la función sqrt no está definida para ningún número negativo y sqrt (-1.0f) si IEEE-754
produce un valor NaN
. Pero 1.0f / 0 es Inf
.
Pero por alguna razón, este no es el caso en IEEE-754
. Debe haber una razón para esto, tal vez algunas razones de optimización o compatibilidad.
¿Entonces cuál es el punto?
En matemáticas, la división por cero no está definida porque el cero no tiene signo, por lo tanto, dos resultados son igualmente posibles, y exclusivos: infinito negativo o infinito positivo (pero no ambos).
En (la mayoría) de la informática, 0.0 tiene un signo. Por lo tanto, sabemos de qué dirección nos estamos acercando, y qué signo tendría el infinito. Esto es especialmente cierto cuando 0.0 representa un valor distinto de cero demasiado pequeño para ser expresado por el sistema, como ocurre frecuentemente.
La única vez que NaN sería apropiado es si el sistema sabe con certeza que el denominador es verdaderamente, exactamente cero. Y no puede hacerlo a menos que haya una forma especial de designar eso, lo que agregaría gastos generales.
No estoy seguro de por qué creerías que esto es una tontería.
La definición simplista de a / b
, al menos para b
diferente de cero, es el número único de b
s que debe restarse de a
antes de llegar a cero.
Ampliando eso al caso donde b
puede ser cero, el número que tiene que ser restado de cualquier número distinto de cero para llegar a cero es de hecho infinito, porque nunca llegarás a cero.
Otra forma de verlo es hablar en términos de límites. Como un número positivo n
acerca a cero, la expresión 1 / n
acerca al "infinito". Notarás que he citado esa palabra porque soy un firme creyente en no propagar la ilusión de que el infinito es en realidad un número concreto :-)
NaN
está reservado para situaciones donde el número no puede ser representado (incluso aproximadamente) por ningún otro valor (incluidos los infinitos), se considera distinto de todos esos otros valores.
Por ejemplo, 0 / 0
(usando nuestra definición simplista anterior) puede restar cualquier cantidad de b
s de a
para llegar a 0. Por lo tanto, el resultado es indeterminado: podría ser 1, 7, 42, 3.14159 o cualquier otro valor.
De forma similar, no se pueden representar elementos como la raíz cuadrada de un número negativo, que no tiene ningún valor en el plano real utilizado por IEEE754 (hay que ir al plano complejo para eso).
NOTA : Reescribí esto después de un comentario valioso de @Cubic.
Creo que la respuesta correcta a esto tiene que venir del cálculo y la noción de límites. Considere el límite de f(x)/g(x)
como x->0
bajo la suposición de que g(0) == 0
. Hay dos casos amplios que son interesantes aquí:
- Si
f(0) != 0
, entonces el límite comox->0
es más o menos infinito, o es indefinido. Sig(x)
toma ambos signos en la vecindad dex==0
, entonces el límite no está definido (los límites izquierdo y derecho no concuerdan). Sig(x)
tiene solo un signo cerca de 0, sin embargo, el límite se definirá y será infinito positivo o negativo. Más sobre esto más tarde. - Si
f(0) == 0
también, entonces el límite puede ser cualquier cosa, incluido el infinito positivo, el infinito negativo, un número finito o indefinido.
En el segundo caso, en general, no puedes decir nada en absoluto. Podría decirse que, en el segundo caso, NaN
es la única respuesta viable.
Ahora, en el primer caso, ¿por qué elegir un signo en particular cuando es posible o puede estar indefinido? Como una cuestión práctica, le da más flexibilidad en casos donde usted sabe algo sobre el signo del denominador, a un costo relativamente bajo en los casos en que no lo hace. Puede tener una fórmula, por ejemplo, donde sepa analíticamente que g(x) >= 0
para todas las x
, digamos, por ejemplo, g(x) = x*x
. En ese caso, el límite está definido y es infinito con un signo igual al signo de f(0)
. Es posible que desee aprovechar eso como una conveniencia en su código. En otros casos, donde no sabe nada sobre el signo de g
, generalmente no puede aprovecharlo, pero el costo aquí es solo que necesita atrapar algunos casos adicionales, infinito positivo y negativo, además de NaN
si quiere verificar completamente su código. Hay algún precio allí, pero no es grande en comparación con la flexibilidad obtenida en otros casos.
¿Por qué preocuparse por las funciones generales cuando la pregunta era sobre "división simple"? Una razón común es que si está calculando su numerador y denominador a través de otras operaciones aritméticas, acumula errores de redondeo. La presencia de esos errores se puede resumir en el formato de fórmula general que se muestra arriba. Por ejemplo f(x) = x + e
, donde x
es la respuesta exacta analíticamente correcta, e
representa el error de redondeo, y f(x)
es el número de punto flotante que realmente tiene en la máquina en la ejecución.