Cómo determinar un número más cercano a un número dado en coma flotante.

Estoy aprendiendo a programar y me encontré con el subproducto interesante de la representación de números de computadora que se muestra por

0.1 + 0.2 = 0.30000000000000004

Al tratar de entender por qué ocurre esto, uno tiene que entender cómo se almacena 0.1 en la computadora. me di cuenta de que

0.1 ( 10 ) = 0.00011001100... ( 2 )
y posteriormente leer que el número de números de punto flotante más cercano a 0.1 son
0.09999999999999999167332731531132594682276248931884765625
0.1000000000000000055511151231257827021181583404541015625
(fuente: https://stackoverflow.com/a/27030789 ).

Curioso sobre cómo determinar realmente estos números, intenté escribir un script de Python para encontrarlos y fracasé miserablemente. Luego recurrí a Google y encontré esta respuesta a una pregunta similar: https://math.stackexchange.com/a/2119191/22276 . Pude seguir la solución por completo en mi caso hasta la parte que calculó los números 14411518807585587 y 14411518807585588. ¿Cómo uso estos números para obtener los dos números circundantes?

Como referencia, mis dos enteros estarían determinados por:

2 4 = .0625 0.1 < .125 = 2 3
causando mi = 4 . Luego calculo la parte entera de

0.1 2 53 ( 4 )
cual es
144 115 188 075 855 872

Aquí es donde estoy atascado.

Parece que esta pregunta es más adecuada para StackOverflow.
En pocas palabras, desea encontrar:
k 2 53 < 0.1 < ( k + 1 ) 2 53
Tal vez estoy siendo denso, pero tengo k 0.1 9007199254740992 = 900719925474099.2 900719925474099 . Esto da 900719925474099 2 53 = 0.09999999999999997779553950749686919152736663818359375 .

Respuestas (2)

Dejar X = 0.1 , y = 0.2 , z = 0.3 . Dejar X , y , z los valores correspondientes redondeados a números de doble precisión. Tienes

    x' = 0.0001100110011001100110011001100110011001100110011001101  (bin)
    x' = 0.1000000000000000055511151231257827021181583404541015625  (dec)
    y' = 0.0011001100110011001100110011001100110011001100110011010  (bin)
    y' = 0.2000000000000000111022302462515654042363166809082031250  (dec)
    z' = 0.0100110011001100110011001100110011001100110011001100110  (bin)
    z' = 0.2999999999999999888977697537484345957636833190917968750  (dec)

Ahora agrega X + y y obtener como sumas brutas

  x'+y' = 0.0100110011001100110011001100110011001100110011001100111 (bin)
  x'+y' = 0.3000000000000000166533453693773481063544750213623046875 (dec)

que se redondearía a

(x'+y')'= 0.010011001100110011001100110011001100110011001100110100 (bin)
(x'+y')'= 0.3000000000000000444089209850062616169452667236328125 (dec)

Eso no X X > 0 y y y > 0 , es decir X , y se redondean, mientras que z z < 0 , es decir z se redondea hacia abajo y X + y está redondeado.

Esto es fantástico. Muchas gracias por tu redacción. Estoy pensando que en mi publicación el valor a continuación 0.1 (es decir, 0.09999999999999999167332731531132594682276248931884765625) es incorrecto. Su valor del redondeo de x a x' coincide con mi cálculo, mientras que mi valor más bajo de x resulta como
0.09999999999999997779553950749686919152736663818359375
. Este último vino del binario
0.0001100110011001100110011001100110011001100110011001100
Acabo de encontrar un aspecto que no está claro. Al sumar las formas binarias redondeadas de 0,1 y 0,2 seguidas de la suma bruta de 0,3 y la forma redondeada de la suma bruta:
0.0001100110011001100110011001100110011001100110011001101
0.0011001100110011001100110011001100110011001100110011010
0.0100110011001100110011001100110011001100110011001100111
0.0100110011001100110011001100110011001100110011001101
. Está claro cómo 0.3 redondea a este último binario, pero no me queda claro por qué perdemos 3 lugares binarios en el redondeo en lugar de dejar los últimos tres 1.
Es decir, ¿por qué elegiría la computadora la suma redondeada [es decir, (x'+y')' (bin)] sobre x'+y' (bin)?
No importa, lo descubrí. Se debe a la convención de redondeo de IEEE 754 y vuelve al exponente que precede a la mantisa.
Fui un poco perezoso y omití los 0 bits finales. La mantisa para doble precisión tiene 53 bits (donde está implícito el bit 1 inicial). Edité la respuesta para incluir los bits cero. (x'+y')' se usa porque el compilador agrega x' e y' y redondea el resultado dando ('x+y')'. Tenga en cuenta que la suma bruta dada tiene más de 53 bits significativos, que se redondean a los 53 bits de (x'+y')'.

Intenté escribir un script de python para encontrarlos y fracasé miserablemente.

En realidad, es bastante trivial mostrar un número de coma flotante exactamente en python (lo probé con 3.5). Convertir un número de coma flotante en una cadena da como resultado directamente una respuesta de "ajuste más corto", pero convertirlo a través de un "Decimal" da la respuesta exacta.

from decimal import Decimal, getcontext
print(Decimal(0.1)) # 0.1000000000000000055511151231257827021181583404541015625 
print(Decimal(0.2)) # 0.200000000000000011102230246251565404236316680908203125

Ahora intentemos sumarlos, tanto exactamente como usando aritmética de coma flotante.

getcontext().prec = 55 # needed to avoid rounding when we add two Decimal's together.
print(Decimal(0.1)+Decimal(0.2)) # 0.3000000000000000166533453693773481063544750213623046875
print(Decimal(0.1+0.2)) # 0.3000000000000000444089209850062616169452667236328125

Vemos que la aritmética de punto flotante ha dado como resultado cierto redondeo.

Finalmente, veamos qué valor obtenemos para la constante 0.3

 print(Decimal(0.3)) # 0.299999999999999988897769753748434595763683319091796875
Dado que algunos números en base 2 pueden tener infinitos números finales en base 10, creo que esta respuesta es algo defectuosa.