2010-06-05 28 views
87

Esto es algo que he tenido en mente durante años, pero nunca me tomé el tiempo para preguntar antes.¿Cuántos números dobles hay entre 0.0 y 1.0?

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 double es un número de punto flotante, y por lo tanto tiene una precisión finita.

Así que las preguntas son:

  1. double ¿Cuántos nú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 hace una diferencia, estoy interesado en la definición de Java de double en particular.

Respuesta

60

s están en formato IEEE-754, por lo tanto tienen una fracción de 52 bits; entre dos potencias adyacentes de dos (incluida una y exclusiva de la siguiente), habrá por lo tanto 2 a la 52 potencia diferente double s (es decir, 4503599627370496 de ellas). Por ejemplo, ese es el número de double distintos entre 0.5 incluido y 1.0 excluido, y exactamente que muchos también se encuentran entre 1.0 incluido y 2.0 excluido, y así sucesivamente.

Contar el doubles entre 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 problemas 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 de NaN), tendrías 1024 veces el double s entre dos - no más de 2**62 en total de todos modos. Excluyendo &c desnormalizado, 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 double (no es un múltiplo exacto de ninguna potencia de dos).Como una aproximación muy útil, ya que la progresión entre las potencias de dos es lineal, se podría decir que dicho rango es 0.1/64 º de la luz entre las potencias circundantes de dos (64 y 128), por lo que cabría esperar sobre

(0.1/64) * 2**52 

distinto double s - que viene a 7036874417766.4004 ... dar o recibir uno o dos ;-).

+0

@Alex: solo para observar, cuando escribí 100 a 100.1 escribí mal. Me refiero a 100 a 101. Básicamente, entre N y N + 1 para arbitraria N. – polygenelubricants

+4

@Alex: así que permítanme aclarar esto: no puede haber más de '2 ** 64' posibles valores dobles (ya que es un 64 bit tipo), y aparentemente una ENORME proporción de esos valores se encuentran entre '0..1'? – polygenelubricants

+9

@polygene, sí y sí: específicamente, alrededor de un cuarto de los posibles valores (para cualquier representación de punto flotante "normal" de cualquier base y exponente frente a fracciones de longitud) se encuentran entre 0.0 y 1.0 (otro cuarto entre 1.0 e infinito, y la mitad restante en la mitad negativa del eje real). Esencialmente, la mitad de los valores del exponente (con un sesgo normal, a medio camino dentro de su rango) representan poderes negativos de la base, por lo tanto, números <1.0. –

2
  1. 2^53 - el tamaño del significando/mantisa de un número de punto flotante de 64 bits que incluye el bit oculto.
  2. Aproximadamente sí, ya que el sifnificando está fijo pero el exponente cambia.

Consulte el wikipedia article para obtener más información.

+0

Su respuesta para 2 contradice la forma en que entiendo el funcionamiento de FP. – polygenelubricants

+0

Creo que '1' es incorrecto porque el bit oculto es siempre uno, por lo tanto,' 2^52', ** no ** '2^53' _distinct_ values ​​(entre potencias adyacentes de dos, uno incluido y el siguiente excluido - ** no ** entre 0.0 y 1.0!). –

3

El artículo Java's new math, Part 2: Floating-point numbers de IBM ofrece el siguiente fragmento de código para resolver este (en balsas, pero sospecho que trabaja en dobles también):

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 carrozas 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 son aproximadamente 0.0000001 aparte. Esta distancia se denomina ULP para la unidad de menor precisión o unidad en el último lugar.

+0

Sí, pero eso es para 'float', _not_' double' - 'float's tiene una fracción de 23 bits, por lo que' 2 ** 23 -> 8388608' valores diferentes entre potencias adyacentes de dos (el "inclusive" parte, por supuesto, significa que debes contar una más, la siguiente potencia de dos). 'double's tiene fracciones de 52 bits! –

+1

@Alex: supongo que tendré que dejar el programa (modificado para dobles) ejecutándose hasta el final del universo o así antes de que pueda obtener los resultados ... :( –

+1

Me siento tonto, acabo de escribir el 'doble 'equivalente y pensamiento" Oye, responderé mi propia pregunta en unos 5 minutos ... " – polygenelubricants

1

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

Esto significa que tenemos que considerar:

  1. Mantisa es de 52 bit
  2. exponente es 11 número de bit con 1023 sesgo (es decir, con 1023 añadió a ella)
  3. Si el exponente es todo 0 y la mantisa no es cero, entonces se dice que el número no es normalizado

Esto significa básicamente que hay un total de 2^62-2^52 + 1 de posibles representaciones dobles que de acuerdo con la norma se encuentran entre een 0 y 1. Tenga en cuenta que 2^52 + 1 es para eliminar los casos de los números no normalizados.

Recuerde que si mantisa es positivo pero exponente es número negativo es positiva pero inferior a 1 :-)

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

35

Cada double valor cuya representación es entre 0x0000000000000000 y 0x3ff0000000000000 se encuentra 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 las representaciones entre 0x3ff0000000000000 y 0x400000000000000; eso es 2^52 valores distintos.

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

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

99999999999999982163600188718701095... 

y

10000000000000000159028911097599180... 
+0

+1, para una respuesta exacta bien respaldada. (Si eres exigente con el recuento de los puntos finales, recuerda que +0.0 y -0.0 tienen representaciones distintas.) –

+1

+1, ¡un final tan retorcido! ¡Me sentí como si estuviera leyendo un guión de M. Night Shyamalan! – polygenelubricants

6

Otros ya han explicado que hay alrededor de 2^62 dobles en el intervalo [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 son < 1.0.)

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

Cuestiones relacionadas