¿Cuántos números dobles hay entre 0.0 y 1.0?

94

Esto es algo que ha estado en mi mente durante años, pero nunca antes me había tomado el tiempo de preguntar.

Muchos generadores de números (pseudo) aleatorios generan un número aleatorio entre 0.0 y 1.0. Matemáticamente hay números infinitos en este rango, pero doublees un número de coma flotante y, por lo tanto, tiene una precisión finita.

Entonces las preguntas son:

  1. ¿Cuántos doublenúmeros hay entre 0.0 y 1.0?
  2. ¿Hay tantos números entre 1 y 2? ¿Entre 100 y 101? ¿Entre 10 ^ 100 y 10 ^ 100 + 1?

Nota: si marca la diferencia, estoy interesado en la definición de Java doubleen particular.

poligenelubricantes
fuente

Respuestas:

68

Los Java doubleestán en formato IEEE-754 , por lo que tienen una fracción de 52 bits; entre dos potencias adyacentes de dos (incluida una y excluida la siguiente), habrá 2 doubleelevado a la 52 potencia s diferentes (es decir, 4503599627370496 de ellas). Por ejemplo, ese es el número de doubles distintos entre 0.5 incluido y 1.0 excluido, y exactamente ese número también se encuentra entre 1.0 incluido y 2.0 excluido, y así sucesivamente.

Contar doublesentre 0.0 y 1.0 es más difícil que hacerlo entre potencias de dos, porque hay muchas potencias de dos incluidas en ese rango y, además, uno se mete en los espinosos temas de los números desnormalizados. 10 de los 11 bits de los exponentes cubren el rango en cuestión, por lo que, incluidos los números desnormalizados (y creo que algunos tipos deNaN ) tendrías 1024 veces la doubles entre potencias de dos, no más que 2**62en total de todos modos . Excluyendo los & c desnormalizados, creo que el recuento sería 1023 veces 2**52.

Para un rango arbitrario como "100 a 100.1" es aún más difícil porque el límite superior no se puede representar exactamente como a double(no es un múltiplo exacto de ninguna potencia de dos). Como una práctica aproximación, dado que la progresión entre potencias de dos es lineal, se podría decir que dicho rango es0.1 / 64 el intervalo entre las potencias circundantes de dos (64 y 128), por lo que esperaría aproximadamente

(0.1 / 64) * 2**52

distintos doubles - que viene a 7036874417766.4004... más o menos uno o dos ;-).

Alex Martelli
fuente
@Alex: solo para notar, cuando escribí 100 a 100.1 escribí mal. Quise decir 100 a 101. Básicamente, entre N y N + 1 para N. arbitrario
polygenelubricants
4
@Alex: déjame aclarar esto: no puede haber más de 2**64posibles valores dobles (ya que es un tipo de 64 bits), y aparentemente una ENORME proporción de esos valores se encuentra entre 0..1?
polygenelubricants
9
@polygene, sí y sí - específicamente, aproximadamente una cuarta parte de los valores posibles (para cualquier representación de punto flotante "normal" de cualquier base y exponente frente a longitudes de fracción) se encuentran entre 0.0 y 1.0 (otra cuarta parte entre 1.0 e infinito, y el la mitad restante en la mitad negativa del eje real). Esencialmente, la mitad de los valores del exponente (con un sesgo normal, a la mitad de su rango) representan potencias negativas de la base, por lo tanto, números <1.0.
Alex Martelli
8
@polygenelubricants: para muchas aplicaciones, el rango entre 0 y 1 es mucho, mucho más importante e interesante que el rango entre 100 y 101, por eso obtiene una mayor participación de los valores. Por ejemplo, en física, a menudo tienes que lidiar con valores ridículamente pequeños como la constante graviacional de Newton en 6.67e-11. Tener buena precisión allí es más útil que entre 100 y 101. Lea floating-point-gui.de para obtener más información.
Michael Borgwardt
1
También puede escalar cualquier número entre 0.0 y 1.0, realizando un seguimiento de la escala por separado, lo que produce menos errores en el cálculo. ¡Es bueno cuando toda la recta numérica se puede mapear entre dos números!
Codekaizen
42

Todo doublevalor cuya representación está entre 0x0000000000000000y se 0x3ff0000000000000encuentra en el intervalo [0.0, 1.0]. Eso es (2 ^ 62 - 2 ^ 52) valores distintos (más o menos un par dependiendo de si cuenta los puntos finales).

El intervalo [1.0, 2.0] corresponde a representaciones entre 0x3ff0000000000000y 0x400000000000000; eso es 2 ^ 52 valores distintos.

El intervalo [100.0, 101.0] corresponde a representaciones entre 0x4059000000000000y 0x4059400000000000; eso es 2 ^ 46 valores distintos.

No hay dobles entre 10 ^ 100 y 10 ^ 100 + 1 . Ninguno de esos números se puede representar con precisión doble, y no hay dobles que se encuentren entre ellos. Los dos números de doble precisión más cercanos son:

99999999999999982163600188718701095...

y

10000000000000000159028911097599180...
Stephen Canon
fuente
+1, para una respuesta exacta bien fundamentada. (Si es exigente con el recuento de los puntos finales, recuerde que +0.0 y -0.0 tienen representaciones distintas).
Jim Lewis
1
+1, ¡un final tan retorcido! ¡Me sentí como si estuviera leyendo un guión de M. Night Shyamalan!
polygenelubricants
7

Otros ya han explicado que hay alrededor de 2 ^ 62 dobles en el rango [0.0, 1.0].
(No es realmente sorprendente: hay casi 2 ^ 64 dobles finitos distintos, de los cuales la mitad son positivos, y aproximadamente la mitad de los que son <1,0).

Pero menciona generadores de números aleatorios: tenga en cuenta que un generador de números aleatorios que genere números entre 0,0 y 1,0 no puede, en general, producir todos estos números; normalmente solo producirá números de la forma n / 2 ^ 53 con n un entero (consulte, por ejemplo, la documentación de Java para nextDouble ). Por lo tanto, generalmente solo hay alrededor de 2 ^ 53 (+/- 1, según los puntos finales que se incluyan) valores posibles para la random()salida. Esto significa que la mayoría de los dobles en [0.0, 1.0] nunca se generarán.

Mark Dickinson
fuente
3

El artículo Nuevas matemáticas de Java, Parte 2: Números de punto flotante de IBM ofrece el siguiente fragmento de código para resolver esto (en flotantes, pero sospecho que también funciona para dobles):

public class FloatCounter {

    public static void main(String[] args) {
        float x = 1.0F;
        int numFloats = 0;
        while (x <= 2.0) {
            numFloats++;
            System.out.println(x);
            x = Math.nextUp(x);
        }
        System.out.println(numFloats);
    }
}

Tienen este comentario al respecto:

Resulta que hay exactamente 8.388.609 flotantes entre 1.0 y 2.0 inclusive; grande pero difícilmente la infinidad incontable de números reales que existen en este rango. Los números sucesivos están separados por aproximadamente 0,0000001. Esta distancia se denomina ULP por unidad de menor precisión o unidad en último lugar.

Mark Rushakoff
fuente
Sí, pero eso es para float, no double - floattienen una fracción de 23 bits, por lo que 2**23 -> 8388608valores diferentes entre potencias adyacentes de dos (la parte "inclusiva" por supuesto significa que tienes que contar una más, la siguiente potencia de dos). doubles tienen fracciones de 52 bits!
Alex Martelli
1
@Alex: Supongo que tendré que dejar el programa (modificado para dobles) funcionando hasta el final del universo antes de poder obtener los resultados ... :(
Mark Rushakoff
1
Me siento tonto; Escribí el doubleequivalente y pensé "Oye, responderé mi propia pregunta en unos 5 minutos ..."
polygenelubricants
1
@polygene: Esto se siente como un problema del Proyecto Euler donde el enfoque obvio no es factible de calcular, pero debe haber alguna fórmula brillantemente simple para resolver el caso arbitrario ...
Mark Rushakoff
2
tal vez no con una supercomputadora verdaderamente sobrealimentada: en una máquina que toma solo un nanosegundo para ejecutar el ciclo interno, contar con doublepotencias adyacentes de dos tomaría aproximadamente 52 días ( printlnpor supuesto, es muy poco probable que funcione tan rápido sin importar qué, así que supongamos que una declaración desaparece ;-). Creo que es factible tomar un año o menos en una máquina potente pero realista ;-).
Alex Martelli
2
  1. 2 ^ 53: el tamaño del significado / mantisa de un número de coma flotante de 64 bits que incluye el bit oculto.
  2. Aproximadamente sí, ya que el sifnificando es fijo pero el exponente cambia.

Consulte el artículo de wikipedia para obtener más información.

Yann Ramin
fuente
Su respuesta para 2 contradice cómo entiendo el funcionamiento de FP.
polygenelubricants
Creo que 1es un error ya que el bit oculto es siempre uno - por lo tanto, 2^52, no 2^53 distintos valores (entre los poderes adyacentes de dos, uno y se incluye la siguiente excluidos - No ! Entre 0,0 y 1,0).
Alex Martelli
1

El doble de Java es un número IEEE 754 binary64.

Esto significa que debemos considerar:

  1. Mantissa es de 52 bits
  2. El exponente es un número de 11 bits con un sesgo de 1023 (es decir, con 1023 agregados)
  3. Si el exponente es todo 0 y la mantisa no es cero, se dice que el número no está normalizado.

Esto básicamente significa que hay un total de 2 ^ 62-2 ^ 52 + 1 de posibles representaciones dobles que, según el estándar, están entre 0 y 1. Tenga en cuenta que 2 ^ 52 + 1 es para eliminar los casos de los no normalizados. números.

Recuerde que si la mantisa es positiva pero el exponente es negativo, el número es positivo pero menor que 1 :-)

Para otros números es un poco más difícil porque los números enteros de borde pueden no ser representables de manera precisa en la representación IEEE 754, y porque hay otros bits usados ​​en el exponente para poder representar los números, por lo que cuanto mayor es el número, menor los diferentes valores.

njsf
fuente