Charla aleatoria: ¿cómo explicarle a la novia por qué 0.2 + 0.1 no es igual a 0.3 en la computadora?


Autor | Hablando de programación

Fuente | Programación de conversación aleatoria (ID: mhcoding)


¿Por qué cuando usamos el navegador de la computadora para calcular 0.2 + 0.1, la solución es 0.30000000000000004, pero el resultado de 0.1 + 0.6 es 0.7?

Este problema siempre ha sido un problema clásico, incluso existe un sitio web cuyo nombre de dominio es https://0.30000000000000004.com/, lo que explica principalmente este problema.

En este sitio web, se enumeran los resultados de calcular 0.2 + 0.1 en varios lenguajes de programación, y algunos se seleccionan de la siguiente manera:

Se puede ver que en varios idiomas, los resultados de calcular 0.2 + 0.1 son sorprendentemente consistentes, que es este mágico 0.30000000000000004.

De hecho, cuando usamos la consola del navegador (F12) para realizar los cálculos, usamos el lenguaje JavaScript para realizar los cálculos, por lo que el fenómeno anterior no tiene nada que ver con el lenguaje de programación específico en el análisis final.

El principal problema es cómo representar decimales en computadoras y cómo realizar operaciones decimales.

Sabemos que las computadoras solo reconocen 0 y 1 [Por qué las computadoras solo reconocen 0 y 1], y el contenido del mundo real debe convertirse en sistemas binarios si desea almacenarlos, calcularlos o mostrarlos en una computadora. En el mundo real, los números incluyen principalmente números enteros y decimales.

En el artículo anterior [Por qué las computadoras usan complementos para almacenar datos], presentamos que hay muchas formas de representar números enteros en computadoras, como códigos originales, códigos inversos y complementos.

Los enteros incluyen enteros positivos, enteros negativos y cero. Los enteros almacenados en computadoras se dividen en números con signo y números sin signo.

Para los números sin signo, no importa qué método de codificación se utilice. Para los números con signo, el más utilizado es el complemento.

Entonces, si un número decimal quiere obtener su complemento binario, necesita obtener su código original correspondiente a través de un algoritmo determinado.


Decimal a binario


Primero, echemos un vistazo, ¿cómo convertir un entero decimal en un entero binario?

La conversión de un entero decimal a un entero binario adopta el método de "dividir por 2 y tomar el resto, ordenar en orden inverso".

El enfoque específico es:

  • Divida un entero decimal por 2 para obtener un cociente y un resto;

  • Use 2 para quitar el cociente nuevamente, y obtendrá un cociente y un resto, y así sucesivamente, hasta que el cociente sea menor que 1.

  • Luego, el resto obtenido primero se usa como el bit de baja significancia del número binario, y el resto obtenido más tarde se usa como el bit de alta significancia del número binario y se organiza en secuencia.

Por ejemplo, queremos convertir 127 a binario, de la siguiente manera:

Entonces, ¿cómo se calcula la conversión de un número decimal en un número binario?

La conversión de decimales decimales en decimales binarios adopta el método de "multiplicar por 2 y redondear en orden".

El método específico es: * Multiplica la fracción decimal por 2 para obtener el producto * Saca la parte entera del producto, luego multiplica la parte fraccionaria restante por 2 para obtener otro producto * Luego saca la parte entera del producto y hazlo hasta que el producto esté en La parte fraccionaria de es cero, en este momento 0 o 1 es el último dígito del binario. O hasta que se alcance la precisión requerida.

Por lo tanto, el valor binario de 0.625 en decimal es 0.101.

No todos los números se pueden representar en binario


Sabemos cómo convertir un número decimal en binario, entonces, ¿podemos usar binario directamente para representar el decimal en el cálculo?

En nuestro ejemplo anterior, 0.625 es una columna especial, por lo que utilizando el mismo algoritmo, calcule el binario correspondiente a 0.1?

Encontramos que hay un bucle infinito en la representación binaria de 0.1, es decir (0.1) 10 = (0.000110011001100 ...) 2

En este caso, la computadora no puede representar con precisión 0.1 en binario.

En otras palabras, para un número como 0.1, no podemos convertirlo en cierto número binario.

IEEE 754


Para resolver el problema de que algunos decimales no se pueden representar con precisión en binario, existe la especificación IEEE 754.

El estándar IEEE para aritmética de coma flotante binaria (IEEE 754) es el estándar aritmético de coma flotante más utilizado desde la década de 1980 y es adoptado por muchas CPU y unidades aritméticas de coma flotante.

Los números en coma flotante y los decimales no son exactamente lo mismo, en realidad existen dos tipos de representaciones decimales en las computadoras: coma fija y coma flotante. Porque en el caso de la misma cantidad de dígitos, el rango de representación de los números de coma fija es menor que el de los números de coma flotante. Entonces, en informática, los números de punto flotante se utilizan para representar el valor aproximado de los números reales.

IEEE 754 especifica cuatro formas de representar valores de punto flotante: precisión simple (32 bits), precisión doble (64 bits), precisión simple extendida (por encima de 43 bits, rara vez se usa) y precisión doble extendida (79 bits) Lo anterior generalmente se implementa en 80 bits).

Los más utilizados son los números de coma flotante de precisión simple de 32 bits y los números de coma flotante de precisión doble de 64 bits.

IEEE no resolvió el problema de que los decimales no se podían representar con precisión, sino que solo propuso una forma de usar valores aproximados para representar decimales e introdujo el concepto de precisión.

Un número de coma flotante es una secuencia de bits compuesta por una cadena de ceros y 1, que lógicamente usa triples {S, E, M} para representar un número N, como se muestra en la siguiente figura:

  • S (signo) representa el bit de signo de N. El valor correspondiente s satisface: cuando n> 0, s = 0; cuando n≤0, s = 1.

  • E (exponente) representa el bit de exponente de N, que se encuentra entre S y M. El valor correspondiente e también puede ser positivo o negativo.

  • M (mantisa) representa la mantisa de N, que se encuentra al final de N. M también se llama significativo, coeficiente o incluso "decimal".

Entonces, el valor real n del número de coma flotante N se representa mediante la siguiente fórmula:

La fórmula anterior parece muy complicada. El bit de signo y el bit de mantisa son relativamente fáciles de entender, pero el bit de exponente no es tan fácil de entender.

De hecho, no necesita estar demasiado enredado con esta fórmula. Solo necesita saber que para los números de punto flotante de precisión simple, solo puede usar caracteres de 32 bits para representar un número, y los números de punto flotante de doble precisión solo pueden usar 64 bits para representar un número.

Para esos números binarios con bucles infinitos, la computadora usa números de punto flotante para retener un cierto número de números significativos, por lo que este valor solo puede ser un valor aproximado, no un valor verdadero.

En cuanto a cómo se debe calcular el número de punto flotante IEEE 754 correspondiente a un número, no es el tema central de este artículo. No lo repetiré aquí. El proceso aún es relativamente complicado. Necesita realizar orden, suma de mantisa, normalización, redondeo y juicio de desbordamiento.

Pero estas cosas no necesitan entenderse en detalle, solo necesitamos saber que la representación de decimales en la computadora es un número aproximado, no un valor verdadero. Dependiendo de la precisión, el grado de aproximación también es diferente.

Por ejemplo, el número decimal 0.1, el binario correspondiente al número de coma flotante de doble precisión es: 0.000110011001100110011001100110011001100110011001100110011001.

El decimal de 0.2 es 0.001100110011001100110011001100110011001100110011001100110011.

Así que suma los dos juntos:

Después de convertir a decimal, obtenemos: 0.30000000000000004!

Evita la pérdida de precisión


En Java, float se usa para representar números de punto flotante de precisión simple y double se usa para representar números de punto flotante de precisión doble, que son valores aproximados.

Por lo tanto, en el código Java, no use float o double para realizar cálculos de alta precisión, especialmente cálculos de cantidad, de lo contrario es fácil causar la pérdida de activos.

Para resolver estos problemas de precisión, BigDecimal se proporciona en Java para cálculos precisos.


Materiales de referencia:

https://0.30000000000000004.com/

https://zh.wikipedia.org/zh-hans/IEEE_754

https://www.h-schmidt.net/FloatConverter/IEEE754.html

更多精彩推荐
☞服!AI 让兵马俑“活”起来,颜值惊艳!
☞华为 Mate 40/Pro 系列全球发布会日期定了..... | 每日趣闻
☞对话阿里云:开源与自研如何共处?
☞AI 还原康乾盛世三代皇帝的样貌,简直太太太好玩了!
☞如何应对云原生之旅中的安全挑战?
☞观点 | 回顾以太坊近期及中期扩容路线图,展望 rollup 作为中心的以太坊路线图
点分享点点赞点在看

Supongo que te gusta

Origin blog.csdn.net/csdnsevenn/article/details/109126922
Recomendado
Clasificación