2 votos

Calcular el número total de números en coma flotante en un rango determinado

Esto se inspiró en esta pregunta de Stack Overflow .

Actualmente está en espera como off-topic y downvoted, así que por si acaso se borra aquí está la captura de pantalla (para nosotros los menos mortales con menos de 10K rep en ese sitio): enter image description here

El programa está tratando de enumerar todos los double valores entre estos dos números:

0.009999999999999999999999999999
0.099999999999999999999999999999

El código de la captura de pantalla no lo hará realmente, pero dejemos eso de lado por ahora.

Para aquellos que no estén familiarizados con el sistema de tipos de C#, un doble es un número en coma flotante (que se utiliza para representar números reales) con los siguientes atributos:

El tipo de valor Double representa un número de 64 bits de doble precisión con valores que van de 1,79769313486232e308 negativo a 1,79769313486232e308 positivo, así como cero positivo o negativo, PositiveInfinity, NegativeInfinity y no un número (NaN). Está pensado para representar valores extremadamente grandes (como las distancias entre planetas o galaxias) o extremadamente pequeños (la masa molecular de una sustancia en kilogramos) y que a menudo son imprecisos (como la distancia de la Tierra a otro sistema solar). El tipo Double cumple la norma IEC 60559:1989 (IEEE 754) para la aritmética binaria de punto flotante.

Ahora, obviamente, hay un número infinito de números reales entre estos valores, pero sólo un número finito de números en coma flotante (ya que cualquier número en un ordenador real sólo puede tener, obviamente, un número finito de dígitos).

La otra cosa que hay que entender es Doble.Epsilon que es la siguiente constante:

El valor de la propiedad Epsilon refleja el menor valor positivo de Double que es significativo en operaciones numéricas o comparaciones cuando el valor de la instancia Double es cero...

Más concretamente, el formato de punto flotante consta de un signo, una mantisa o significante de 52 bits y un exponente de 11 bits. Como muestra el siguiente ejemplo, el cero tiene un exponente de -1022 y una mantisa de 0. Epsilon tiene un exponente de -1022 y una mantisa de 1. Esto significa que Epsilon es el menor valor positivo de Double mayor que cero y representa el menor valor posible y el menor incremento posible para un Double cuyo exponente es -1022.

Mi pregunta, entonces: ¿cuántos números dobles hay entre 0,0099999999999999999999 y 0,099999999999999999999999999999?

Si no me equivoco, creo que preguntar cuántos números hay entre 0,009999999999999999999999 y 0,0999999999999999999999999999 es exactamente lo mismo que preguntar cuántos números hay entre 0,0 y 0,09 (que además es bastante más cómodo de leer y trabajar).

Como experimento, he probado el siguiente programa para tener una mejor idea del tamaño del número (sin esperar que se complete la ejecución):

double num = 0.0;
long count = 0;

// Loop through all of the double numbers from 0.0 to 0.09
while (num <= 0.09)
{
    // Add the smallest possible increment
    num += Double.Epsilon;

    // Keep count of how many there are
    count++;
}

// Print the number at the end
Console.WriteLine(count);

Detuve el programa después de dejarlo correr por un tiempo; el número actual era 1.86324012918674E-314 y el conteo era 3,771,240,006.

Sospecho fuertemente que podrías hacer alguna forma de aritmética en los exponentes o posiblemente establecer esto como una permutación de algún tipo; ¿hay una ecuación que pueda describir este problema?

4voto

Shabaz Puntos 403

Tienes que mirar la estructura de cómo se almacena un número de punto flotante. En la norma IEEE 754 se define que un flotante de 64 bits tiene un bit de signo $S$ , $11$ bits de exponente $E$ y $53$ bits de mantisa $M$ . El primer bit de la mantisa es siempre $1$ y no se almacena, por lo que sólo $52$ los bits se almacenan. El valor es $(-1)^S \cdot 2^{E-1023}\cdot M$ donde $M$ está en binario con el punto radial después del primer bit (no almacenado). Esto da que hay $2^{52}$ valores de $M$ por lo que para cada valor del exponente obtenemos $2^{52}$ números de punto flotante.

Para contar los números entre $n_1=0.009999999999999999999999999999$ y $n_2= 0.099999999999999999999999999999$ primero contamos los pasos completos del exponente, y luego manejamos los pasos parciales al final. $n_1 \lt 2^{-6}=0.015625$ y $n_2\gt 2^{-4}=0.0625$ por lo que obtenemos $3 \cdot 2^{52}$ números allí. En el paso donde la parte del exponente es $2^{-7}$ el $\epsilon$ est $2^{-59}$ por lo que hay $2^{59}(0.015625-n_1)=3242591731706757$ . En el paso donde el exponente es $2^{-3}$ el $\epsilon$ est $2^{-55}$ , por lo que hay $2^{55}(n_2-0.0625)=13510798882111487$ . Si mis habilidades de copiar y pegar funcionan el total es $30264189495929732$ Creo que el redondeo es correcto. No me importa intentar enumerarlas todas. Definitivamente no es lo mismo que de $0$ a $0.09$ porque eso tiene más de $1000$ pasos de un factor dos, por lo que sobre $1000 \cdot 2^{52}$ números. En coma flotante los números son muy densos cerca del cero. Usted está añadiendo un tramo cerca de cero, por lo que la adición de un montón de números y no la eliminación de casi tantos ya que el tramo eliminado es "lejos de cero".

Si se dispone de la herramienta adecuada, se puede obtener la representación binaria de $n_1$ y $n_2$ y restarlos. Como las representaciones en coma flotante están ordenadas de la misma manera que los valores correspondientes, eso te dará la cuenta. Recuerda que debes restar uno porque no debes contar ninguno de los extremos.

i-Ciencias.com

I-Ciencias es una comunidad de estudiantes y amantes de la ciencia en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros usuarios, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X