¿Qué tipos de números son representables en coma flotante binario?

He leído mucho sobre carrozas, pero todo está innecesariamente involucrado. Creo que lo entiendo bastante bien, pero hay una cosa de la que me gustaría estar seguro:

Sé que, las fracciones de la forma 1/pow(2,n) , con n un entero, se pueden representar exactamente en números de coma flotante. Esto significa que si agrego 1/32 a sí mismo 32 millones de veces, obtendría exactamente 1,000,000 .

¿Qué tal algo como 1/(32+16) ? Es uno sobre la sum de dos poderes de dos, ¿funciona esto? ¿O es 1/32+1/16 que funciona? Aquí es donde estoy confundido, así que si alguien pudiera aclarar eso, lo apreciaría.

La regla se puede resumir así:

  • Un número puede representarse exactamente en binario si la factorización prima del denominador contiene solo 2. (es decir, el denominador es una potencia de dos)

Entonces 1/(32 + 16) no es representable en binario porque tiene un factor de 3 en el denominador. Pero 1/32 + 1/16 = 3/32 es.

Dicho esto, hay más restricciones para ser representable en un tipo de coma flotante. Por ejemplo, solo tienes 53 bits de mantisa en un IEEE double por lo que 1/2 + 1/2^500 no es representable.

Así que puedes hacer la sum de poderes-de-dos siempre que el rango de los exponentes no abarque más de 53 poderes.


Para generalizar esto a otras bases:

  • Un número puede representarse exactamente en la base 10 si la factorización prima del denominador consiste en solo 2 y 5.

  • Un número racional X puede representarse exactamente en la base N si la factorización prima del denominador de X contiene solo números primos que se encuentran en la factorización de N

Se puede representar un número finito en el formato común de doble precisión IEEE 754 si y solo si es igual a M • 2 e para algunos números enteros M y e tales que -2 53 53 y -1074 ≤ e ≤ 971.

Para precisión simple, -2 24 24 y -149 ≤ e ≤ 104.

Para la precisión doble, estas son consecuencias de los hechos de que el formato de doble precisión utiliza 52 bits para almacenar un significado (que normalmente tiene 53 bits debido a un 1 implícito) y usa 11 bits para almacenar un exponente. 11 bits codifica números de 0 a 2047, pero 0 y 2047 se excluyen para fines especiales, y el número codificado está sesgado por 1023, por lo que representa exponentes imparciales de -1022 a 1023. Sin embargo, estos exponentes imparciales son para significands en el intervalo [1, 2), y esos significandos tienen fracciones. Para express el significado como un entero, ajusté el rango del exponente por 52. La precisión simple es similar, con 23 bits para almacenar un significado de 24 bits, 8 bits para el exponente y un sesgo de 127.

Expresar los números representables usando un número entero multiplicado por una potencia de dos en lugar del significado fraccionario más común y simplifica la teoría de algunos números y otros razonamientos sobre las propiedades de coma flotante. Lo utilicé en esta respuesta porque permite que el conjunto de valores representables se exprese de manera concisa.

Los números de punto flotante se representan literalmente con la forma:

 1.m * 2^e 

Donde 1.m es una fracción binaria y e es un entero positivo o negativo.

Como tal, puede representar 1/32 + 1/16 exactamente, como:

 1.1000000 * 2^-4 

( 1.10 es la fracción binaria equivalente a 1.5.) 1/48 , sin embargo, no es representable en este formato.

Un punto aún no mencionado es que, semánticamente, un número de coma flotante se puede considerar mejor que representa un rango de valores. El rango de valores tiene un punto central definido con precisión, y la especificación IEEE generalmente requiere que el resultado de un cálculo en coma flotante sea el número cuyo rango contenga el punto en que uno operará sobre los puntos centrales de los números originales, pero en la secuencia:

   doble N1 = 0.1;
   flotante N2 = (flotante) N1;
   doble N3 = N2;

N2 es la representación no ambigua correcta de precisión única del valor que se ha representado en N1, a pesar del requisito tonto del lenguaje para usar un molde explícito. N3 representará uno de los valores que N2 podría representar (la especificación de idioma elige el valor double cuyo rango se centra en el centro del rango del float ). Tenga en cuenta que aunque N2 representa el valor de su tipo cuyo rango contiene el valor correcto, N3 no lo hace.

Por cierto, la conversión de un número de una cadena a un flotante en los lenguajes .net y .net parece pasar por una conversión intermedia a double , lo que a veces puede alterar el valor. Por ejemplo, aunque el valor 13571357 es representable como un valor flotante de precisión simple, el valor 13571357.499999999069f se redondea a 13571358 (aunque obviamente está más cerca de 13571357).