En Java, ¿qué significa NaN?


107

Tengo un programa que intenta reducir doublea un número deseado. El resultado que obtengo es NaN.

¿Qué NaNsignifica en Java?


Hay una buena descripción de NaN y de los errores comunes al usar NaN en Java: ppkwok.blogspot.co.uk/2012/11/…
Phil

Si se pregunta "¿de qué sirve NaN?" en Java (o cualquier otro lenguaje), puedo darle un caso de uso donde es muy útil: cuando tengo una matriz 2-D de flotadores, pero mi cálculo no tiene un valor significativo para alguna parte de esa matriz 2-D, Completaré ese valor con "NaN". Esto se puede usar para indicar a los usuarios posteriores de mi cálculo (como cuando se convierte en una imagen rasterizada) "no presten atención al valor en este punto". ¡Muy útil!
Dan H

Por cierto, ¿qué, exactamente, significa "encoger" un doble? Curioso ...
Dan H

Respuestas:


153

Tomado de esta página :

"NaN" significa "no es un número". "Nan" se produce si una operación de coma flotante tiene algunos parámetros de entrada que hacen que la operación produzca algún resultado indefinido. Por ejemplo, 0.0 dividido por 0.0 es aritméticamente indefinido. Sacar la raíz cuadrada de un número negativo tampoco está definido.


16
Además, NaN está definido por el estándar IEEE para aritmética de punto flotante (IEEE 754) de manera bastante explícita, que Java sigue ciegamente. Leer el estándar le abre los ojos a muchas cosas, y los valores múltiples de cero son una de las cosas.
Esko

37
Además, NaNtiene la interesante propiedad de ser el único "número" que no es igual a sí mismo cuando se compara. Por lo tanto, una prueba común (y en muchos idiomas la única) si un número xes NaNes la siguiente:boolean isNaN(x){return x != x;}
quazgar

3
¿Link en respuesta está muerto?
Pang

3
... "Sacar la raíz cuadrada de un número negativo no está definido (en aritmética)" ... ¡No lo es! es en realidad iy algunos lenguajes como Python lo manejan muy bien ... Puede que no sea el caso en javati
Rafael T

5
@RafaelT Yo diría que no está definido en aritmética no compleja. No hay forma de asignar un número complejo a un flotante o doble en Java. Python se escribe dinámicamente, por lo que es posible que solo devuelva un número complejo en este caso.
sstn

19

NaNsignifica "No es un número" y es básicamente una representación de un valor de punto flotante especial en el estándar de punto flotante IEE 754 . NaN generalmente significa que el valor es algo que no se puede expresar con un número de punto flotante válido.

Una conversión dará como resultado este valor, cuando el valor que se convierte es otra cosa, por ejemplo, al convertir una cadena que no representa un número.


¿Conversión de cómo? ¿Con parseFloat()o parseDouble? ¿O algo mas?
Alonso del Arte

14

NaNsignifica "No es un número" y es el resultado de operaciones indefinidas en números de punto flotante como, por ejemplo, dividir cero por cero. (Tenga en cuenta que aunque dividir un número distinto de cero por cero también suele ser indefinido en matemáticas, no da como resultado NaN sino un infinito positivo o negativo).


5

NaNsignifica "No es un número". Es un valor de punto flotante especial que significa que el resultado de una operación no se definió o no se puede representar como un número real.

Consulte aquí para obtener más explicaciones de este valor.




4

No significa un número. Es una representación común de un valor numérico imposible en muchos lenguajes de programación.


4

Ejemplo ejecutable mínimo

Lo primero que debes saber es que el concepto de NaN se implementa directamente en el hardware de la CPU.

Todas las principales CPU modernas parecen seguir IEEE 754, que especifica formatos de punto flotante, y los NaN, que son solo valores flotantes especiales, son parte de ese estándar.

Por lo tanto, el concepto será muy similar en cualquier lenguaje, incluido Java, que solo emite código de punto flotante directamente a la CPU.

Antes de continuar, es posible que desee leer primero las siguientes respuestas que he escrito:

Ahora para alguna acción de Java. La mayoría de las funciones de interés que no se encuentran en el idioma principal viven en el interior java.lang.Float.

Nan.java

import java.lang.Float;
import java.lang.Math;

public class Nan {
    public static void main(String[] args) {
        // Generate some NaNs.
        float nan            = Float.NaN;
        float zero_div_zero  = 0.0f / 0.0f;
        float sqrt_negative  = (float)Math.sqrt(-1.0);
        float log_negative   = (float)Math.log(-1.0);
        float inf_minus_inf  = Float.POSITIVE_INFINITY - Float.POSITIVE_INFINITY;
        float inf_times_zero = Float.POSITIVE_INFINITY * 0.0f;
        float quiet_nan1     = Float.intBitsToFloat(0x7fc00001);
        float quiet_nan2     = Float.intBitsToFloat(0x7fc00002);
        float signaling_nan1 = Float.intBitsToFloat(0x7fa00001);
        float signaling_nan2 = Float.intBitsToFloat(0x7fa00002);
        float nan_minus      = -nan;

        // Generate some infinities.
        float positive_inf   = Float.POSITIVE_INFINITY;
        float negative_inf   = Float.NEGATIVE_INFINITY;
        float one_div_zero   = 1.0f / 0.0f;
        float log_zero       = (float)Math.log(0.0);

        // Double check that they are actually NaNs.
        assert  Float.isNaN(nan);
        assert  Float.isNaN(zero_div_zero);
        assert  Float.isNaN(sqrt_negative);
        assert  Float.isNaN(inf_minus_inf);
        assert  Float.isNaN(inf_times_zero);
        assert  Float.isNaN(quiet_nan1);
        assert  Float.isNaN(quiet_nan2);
        assert  Float.isNaN(signaling_nan1);
        assert  Float.isNaN(signaling_nan2);
        assert  Float.isNaN(nan_minus);
        assert  Float.isNaN(log_negative);

        // Double check that they are infinities.
        assert  Float.isInfinite(positive_inf);
        assert  Float.isInfinite(negative_inf);
        assert !Float.isNaN(positive_inf);
        assert !Float.isNaN(negative_inf);
        assert one_div_zero == positive_inf;
        assert log_zero == negative_inf;
            // Double check infinities.

        // See what they look like.
        System.out.printf("nan            0x%08x %f\n", Float.floatToRawIntBits(nan           ), nan           );
        System.out.printf("zero_div_zero  0x%08x %f\n", Float.floatToRawIntBits(zero_div_zero ), zero_div_zero );
        System.out.printf("sqrt_negative  0x%08x %f\n", Float.floatToRawIntBits(sqrt_negative ), sqrt_negative );
        System.out.printf("log_negative   0x%08x %f\n", Float.floatToRawIntBits(log_negative  ), log_negative  );
        System.out.printf("inf_minus_inf  0x%08x %f\n", Float.floatToRawIntBits(inf_minus_inf ), inf_minus_inf );
        System.out.printf("inf_times_zero 0x%08x %f\n", Float.floatToRawIntBits(inf_times_zero), inf_times_zero);
        System.out.printf("quiet_nan1     0x%08x %f\n", Float.floatToRawIntBits(quiet_nan1    ), quiet_nan1    );
        System.out.printf("quiet_nan2     0x%08x %f\n", Float.floatToRawIntBits(quiet_nan2    ), quiet_nan2    );
        System.out.printf("signaling_nan1 0x%08x %f\n", Float.floatToRawIntBits(signaling_nan1), signaling_nan1);
        System.out.printf("signaling_nan2 0x%08x %f\n", Float.floatToRawIntBits(signaling_nan2), signaling_nan2);
        System.out.printf("nan_minus      0x%08x %f\n", Float.floatToRawIntBits(nan_minus     ), nan_minus     );
        System.out.printf("positive_inf   0x%08x %f\n", Float.floatToRawIntBits(positive_inf  ), positive_inf  );
        System.out.printf("negative_inf   0x%08x %f\n", Float.floatToRawIntBits(negative_inf  ), negative_inf  );
        System.out.printf("one_div_zero   0x%08x %f\n", Float.floatToRawIntBits(one_div_zero  ), one_div_zero  );
        System.out.printf("log_zero       0x%08x %f\n", Float.floatToRawIntBits(log_zero      ), log_zero      );

        // NaN comparisons always fail.
        // Therefore, all tests that we will do afterwards will be just isNaN.
        assert !(1.0f < nan);
        assert !(1.0f == nan);
        assert !(1.0f > nan);
        assert !(nan == nan);

        // NaN propagate through most operations.
        assert Float.isNaN(nan + 1.0f);
        assert Float.isNaN(1.0f + nan);
        assert Float.isNaN(nan + nan);
        assert Float.isNaN(nan / 1.0f);
        assert Float.isNaN(1.0f / nan);
        assert Float.isNaN((float)Math.sqrt((double)nan));
    }
}

GitHub aguas arriba .

Corre con:

javac Nan.java && java -ea Nan

Salida:

nan            0x7fc00000 NaN
zero_div_zero  0x7fc00000 NaN
sqrt_negative  0xffc00000 NaN
log_negative   0xffc00000 NaN
inf_minus_inf  0x7fc00000 NaN
inf_times_zero 0x7fc00000 NaN
quiet_nan1     0x7fc00001 NaN
quiet_nan2     0x7fc00002 NaN
signaling_nan1 0x7fa00001 NaN
signaling_nan2 0x7fa00002 NaN
nan_minus      0xffc00000 NaN
positive_inf   0x7f800000 Infinity
negative_inf   0xff800000 -Infinity
one_div_zero   0x7f800000 Infinity
log_zero       0xff800000 -Infinity

Entonces de esto aprendemos algunas cosas:

  • operaciones flotantes extrañas que no tienen ningún resultado sensible dan NaN:

    • 0.0f / 0.0f
    • sqrt(-1.0f)
    • log(-1.0f)

    generar un NaN.

    En C, en realidad es posible solicitar que se generen señales en tales operaciones feenableexceptpara detectarlas, pero no creo que esté expuesto en Java: ¿Por qué la división de enteros por cero 1/0 da error pero punto flotante 1 / 0.0 devuelve "Inf"?

  • operaciones extrañas que están en el límite de más o menos infinito, sin embargo, dan + - infinito en lugar de NaN

    • 1.0f / 0.0f
    • log(0.0f)

    0.0 casi cae en esta categoría, pero probablemente el problema es que podría ir a más o menos infinito, por lo que se dejó como NaN.

  • si NaN es la entrada de una operación flotante, la salida también tiende a ser NaN

  • hay varios valores posibles de NaN 0x7fc00000, 0x7fc00001, 0x7fc00002, aunque x86_64 parece generar solamente 0x7fc00000.

  • NaN e infinito tienen una representación binaria similar.

    Analicemos algunos de ellos:

    nan          = 0x7fc00000 = 0 11111111 10000000000000000000000
    positive_inf = 0x7f800000 = 0 11111111 00000000000000000000000
    negative_inf = 0xff800000 = 1 11111111 00000000000000000000000
                                | |        |
                                | |        mantissa
                                | exponent
                                |
                                sign

    A partir de esto confirmamos lo que especifica IEEE754:

    • tanto NaN como infinitos tienen exponente == 255 (todos unos)
    • los infinitos tienen mantisa == 0. Por lo tanto, solo hay dos infinitos posibles: + y -, diferenciados por el bit de signo
    • NaN tiene mantisa! = 0. Por lo tanto, hay varias posibilidades, excepto para mantisa == 0 que es infinito
  • Los NaN pueden ser positivos o negativos (bit superior), aunque esto no tiene ningún efecto en las operaciones normales.

Probado en Ubuntu 18.10 amd64, OpenJDK 1.8.0_191.


3

No es un tipo de Java, pero en JS y otros lenguajes que uso es "No es un número", lo que significa que alguna operación hizo que no se convirtiera en un número válido.


3

Literalmente significa "No es un número". Sospecho que algo anda mal con su proceso de conversión.

Consulte la sección No es un número en esta referencia


3

No es un valor de punto flotante válido (por ejemplo, el resultado de la división por cero)

http://en.wikipedia.org/wiki/NaN


Discuto con esta respuesta. Primero: "NaN" ES un valor válido para un flotante IEEE. (Después de todo, está definido en la especificación ... entonces es "válido", ¿verdad?). Segundo: la "división por cero" se puede representar mediante IEEE "Infinito positivo" o "Infinito negativo"; un mejor ejemplo de "NaN" es "cero dividido por cero", como algunas otras respuestas han señalado correctamente.
Dan H

"Valor válido" y "definido en especificaciones" no es lo mismo. Aceptado 0/0.
Vladimir Dyuzhev
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.