Logo Studenta

¿Por qué 0.1 + 0.2 no es igual a 0.3 en la mayoría de los lenguajes de programación?

💡 1 Respuesta

User badge image

Notas de Estudio

Luego de que hayas leído la respuesta de , pasemos a ver ciertos detalles.

Hay dos formas de representar los números no enteros (o los enteros muy grandes) en cualquier sistema numérico posicional: punto fijo y punto flotante.

Si yo tengo un valor, por ejemplo 133133, y lo quiero representar en base diez, puedo escribirlo como 0,030303030,03030303… o como 3,030303×1023,030303…×10−2. En el caso de base diez, la primera representación se conoce como decimal, la segunda como notación científica.

Ambas son aproximaciones (en este caso aproximaciones a 7 cifras significativas).

Si yo trabajo con aproximaciones puedo encontrar algunos problemas. Por ejemplo 1313 es aproximadamente 0,3333330,333333. Si sumo tres veces eso: 13+13+13=113+13+13=1, obtendré 0,333333+0,333333+0,333333=0,99999910,333333+0,333333+0,333333=0,999999≠1. Por otro lado la aproximación de 2323 es 0,6666670,666667. Ahora sí, al trabajar con los valores aproximados a 6 cifras significativas, 13+23=113+23=1, pero 13+132313+13≠23.

Como nosotros trabajamos en decimal, pero los computadores trabajan en binario, cuando yo le digo a un computador:

print 0.1+0.2

Esto es lo que hace. El interprete convierte el valor decimal 0,1d0,1d a su forma binaria. Esta forma binaria será aproximada a 0,0001100110011010b0,0001100110011010b (si tuviera infinitos dígitos binarios, el bloque de 1001 se repetiría indefinidamente, pero como estamos aproximando, hacemos algo similar al 0,6666670,666667, esto es que aproximamos al siguiente dígito porque 110110 es más cercano a 655465536655465536 que a 655365536655365536.

También convierte (aproxima) 0,2d=150,2d=15 a 0,0011001100110011b=13107655360,0011001100110011b=1310765536. Asumo aquí aritmética de punto fijo con 16 bits de precisión. Si sumo esos dos valores obtendré: 0,0100110011001101b=19661655360,0100110011001101b=1966165536.

Estas operaciones, en decimal serían:
0,100006103515625+0,1999969482421875=0,30000305175781250,100006103515625+0,1999969482421875=0,3000030517578125

Finalmente tiene que convertir 0,0100110011001101b0,0100110011001101b a decimal, y el resultado es 0,3000030517578125d0,3000030517578125d, pero lo aproximará a 4 decimales. 0,3000=0,30,3000=0,3.

El resultado será

0.3

En este ejemplo de punto fijo con 16 dígitos binarios después del punto, tenemos que 0.1+0.2 sí es 0.3. Sin embargo 0.1+0.1 no es 0.2. Si yo digo print 0.1+0.1 me imprimirá 0.2, pero si comparo: print 0.1+0.1==0.2 me dirá que el resultado no es el mismo, porque estaré sumando 655465536+655465536=13108655361310765536655465536+655465536=1310865536≠1310765536.

Pero la mayoría de los lenguajes de programación no trabajan en punto fijo. Trabajan en punto flotante.

Entonces 0,1d=1100,1d=110 es representado como 1,1001100110011010b×241,1001100110011010b×2−4, y 0,2d=150,2d=15 es representado como 1,1001100110011010b×231,1001100110011010b×2−3. Aquí estoy tomando punto flotante con 16 bits significativos. Todos los números binarios de punto flotante, comienzan con 1,1,, así que ese dígito no es significativo. En el caso de 0,3d=3100,3d=310 su representación en punto flotante a 16 bits significativos es: 1,0011001100110011b×221,0011001100110011b×2−2.

Sumar en punto flotante requiere un paso de alineación: 1,1001100110011010b×23=11,0011001100110100b×241,1001100110011010b×2−3=11,0011001100110100b×2−4. Ahora puedo sumar y obtengo: 100,1100110011001110b×24100,1100110011001110b×2−4. Ajusto el punto flotante y tengo así: 1,001100110011001110b×221,001100110011001110b×2−2. Pero tengo exceso de bits, así que debo ajustarlos y aproximo a: 1,0011001100110100b×221,0011001100110100b×2−2.

Y este resultado no es 0,3d=3100,3d=310.

Si le pido que me presente el resultado de print 0.1+0.2 me imprimirá 0.3, pero si comparo ambos valores: print 0.1+0.2==0.3 me responderá que es falso.

Hay varios tipos de representaciones. Binario de punto flotante es la más utilizada por la mayoría de lenguajes de programación actuales, pero también existen representaciones binarias de punto fijo; decimal codificado en binario (BCD), decimal de punto fijo, etc. Ciertos lenguajes especializados en finanzas, como el Cobol, se aseguran que 0,1+0,2=0,30,1+0,2=0,3. Pero los procesadores ya tienen codificadas en circuito las operaciones en punto flotante de acuerdo a los estándares IEEE 754, y para los procesadores que no tienen coprocesador matemático, los lenguajes usan IEEE 754 en bibliotecas de código. Esta norma establece dos modelos de punto flotante: precisión sencilla, que usa 32 bits en total y 24 bits de precisión, y precisión doble que usa 64 bits en total y 53 bits de precisión. La tendencia actual es hacia precisión doble.

El funcionamiento es el mismo que en mis ejemplos de 16 bits de precisión, y tiene que ver con ese último bit de aproximación.

¿Por qué se usa IEEE 754 en lugar de punto fijo o decimal codificado en binario?

  1. Porque punto flotante permite un mayor rango de valores que punto fijo.
  2. Porque los algoritmos de punto flotante binario son más fáciles de implementar en hardware o software que los de BCD.
  3. Porque en muchas aplicaciones 0,1, 0,2 y 0,3 son aproximaciones a decimal.
  4. Porque BCD ocupa más bits para una misma precisión.
0
Dislike0

✏️ Responder

FlechasNegritoItálicoSubrayadaTachadoCitaCódigoLista numeradaLista con viñetasSuscritoSobreDisminuir la sangríaAumentar la sangríaColor de fuenteColor de fondoAlineaciónLimpiarInsertar el linkImagenFórmula

Para escribir su respuesta aquí, Ingresar o Crear una cuenta

User badge image

Otros materiales