Quien quiera usar double para definir la cantidad de productos, simplemente empaquete a los chicos y listo

Primero, observe el fenómeno. Cuando se
trata del procesamiento de dos tipos de datos de punto flotante, como flotante o doble, siempre habrá fenómenos extraños de vez en cuando. No sé si le ha prestado atención. Permítame dar algunos ejemplos comunes:

Fenómeno típico (1): el juicio condicional supera las expectativas

System.out.println (1f == 0.9999999f); // Imprimir: falso
System.out.println (1f == 0.99999999f); // Imprimir: verdadero Nani?
Fenómeno típico (2): la conversión de datos supera las expectativas

float f = 1.1f;
double d = (double) f;
System.out.println (f); // Imprimir: 1.1
System.out.println (d); // Imprimir: 1.100000023841858 Nani?
Fenómeno típico (3): los cálculos básicos superan las expectativas

System.out.println (0.2 + 0.7);  
 
// Imprimir: 0.8999999999999999 Nani?
Fenómeno típico (4): los datos en aumento superan las expectativas

float f1 = 8455263f;
for (int i = 0; i <10; i ++) {     System.out.println (f1);     f1 ++; } // print: 8455263.0 // print: 8455264.0 // print: 8455265.0 // print: 8455266.0 // Imprimir: 8455267.0 // Imprimir: 8455268.0 // Imprimir: 8455269.0 // Imprimir: 8455270.0 // Imprimir: 8455271.0 // Imprimir: 8455272.0 float f2 = 84552631f; for (int i = 0; i <10; i ++) {     System.out.println (f2);     f2 ++; } // Imprimir: 8.4552632E7 Nani? ¿No hiciste +1? // Imprimir: 8.4552632E7 Nani? ¿No hiciste +1? // Imprimir: 8.4552632E7 Nani? ¿No hiciste +1? // Imprimir: 8.4552632E7 Nani? ¿No hiciste +1? // Imprimir: 8.4552632E7 Nani? ¿No hiciste +1?













 










// Imprimir: 8.4552632E7 Nani? ¿No hiciste +1?
// Imprimir: 8.4552632E7 Nani? ¿No hiciste +1?
// Imprimir: 8.4552632E7 Nani? ¿No hiciste +1?
// Imprimir: 8.4552632E7 Nani? ¿No hiciste +1?
// Imprimir: 8.4552632E7 Nani? ¿No hiciste +1?
Mira, el uso en estos escenarios simples es difícil de satisfacer nuestras necesidades, por lo que hay muchos pozos crípticos esperando que solucionemos los problemas con los números de punto flotante (incluidos el doble y el flotante).

No es de extrañar que el director técnico dijera cruelmente: si alguien se atreve a utilizar datos de punto flotante (doble / flotante) cuando se trata de elementos como la cantidad de bienes, transacciones de pedidos y cálculos de divisas, ¡vayamos directamente!

 

¿Dónde está la razón?
Tomemos el primer fenómeno típico como ejemplo a analizar:

System.out.println (1f == 0.99999999f);
Usa directamente el código para comparar 1 y 0.99999999, ¡y realmente se imprime verdadero!

 

¿Esto muestra qué? Esto muestra que la computadora no puede distinguir estos dos números en absoluto. ¿Por qué es esto?

Pensemos en ello brevemente:

Sabemos que los dos números de punto flotante ingresados ​​son solo los valores específicos que ven nuestros ojos humanos. Son números decimales que generalmente entendemos. Sin embargo, la capa inferior de la computadora no se calcula de acuerdo con el sistema decimal al calcular Aquellos que han aprendido los principios básicos de cálculo saben que la capa inferior de la computadora se basa en última instancia en 0, 1 binario como 010100100100110011011.

Entonces, para comprender la situación real, debemos convertir estos dos números decimales de coma flotante en espacio binario y echar un vistazo.

Cómo convertir números decimales en coma flotante a binarios y cómo calcularlos, creo que esto debería pertenecer al sentido común básico de conversión hexadecimal por computadora. Debo haberlo aprendido en cursos similares de "Principios de composición por computadora". No los repetiré aquí, y dé los resultados directamente (conviértalo a IEEE 754 Precisión simple de 32 bits, que es la precisión correspondiente al tipo flotante)

1.0 (decimal)
    ↓
00111111 10000000 00000000 00000000 (binario)
    ↓
0x3F800000 (hexadecimal)
0.99999999 (decimal)
    ↓
00111111 10000000 00000000 00000000 (binario)
    ↓
0x3F800000 (hexadecimal) Como se esperaba
, la capa inferior de estos dos números de coma flotante decimal El binario la representación es la misma, no es de extrañar que el resultado del juicio de == devuelva verdadero.

Pero el resultado devuelto por 1f == 0.9999999f es el esperado. Si se imprime falso, también los convertiremos a modo binario para ver la situación:

1.0 (decimal)
    ↓
00111111 10000000 00000000 00000000 (binario)
    ↓
0x3F800000 (hexadecimal)
0.9999999 (decimal)
    ↓
00111111 01111111 11111111 11111110 (binario)
    ↓
0x3F7FFFFE (hexadecimal)
Oh, obviamente, son números binarios diferentes, dicho esto es el resultado de la razón.

Entonces, ¿por qué la representación binaria subyacente de 0.9999999 es: 00111111 10000000 00000000 00000000?

¿No es esto claramente la representación binaria del número de coma flotante 1.0?

Se trata de la precisión de los números de punto flotante.

¡La precisión de los números de coma flotante!
Aquellos que han estudiado los "Principios de la composición de computadoras" deben saber que el método de almacenamiento de números de coma flotante en computadoras sigue el estándar de conteo de números de coma flotante IEEE 754, que se puede expresar en notación científica como:

 

Siempre que se den las tres dimensiones de la información: signo (S), parte de orden (E) y parte de mantisa (M), la representación de un número de coma flotante está completamente determinada, por lo que los dos números de coma flotante, float y double, están en la memoria La estructura de almacenamiento en es la siguiente:

 

 

1. Parte del símbolo (S)

0-positivo 1-negativo

2. Parte del código de paso (E) (parte del exponente):

Para números de coma flotante de tipo flotante, la parte del exponente es de 8 bits, considerando si puede ser positiva o negativa, por lo que el rango del exponente que se puede expresar es -127 ~ 128.
Para números de coma flotante de tipo doble, el exponente La parte es de 11 bits, considerando si puede ser positiva o negativa, por lo que el exponente que se puede expresar El rango es -1023 ~ 1024
3. La parte de mantisa (M):

La precisión de los números de punto flotante está determinada por el número de dígitos de la mantisa:

Para los números de coma flotante de tipo flotante, la parte de la mantisa es de 23 dígitos y la conversión a decimal es 2 ^ 23 = 8388608, por lo que la precisión decimal es de solo 6 a 7 dígitos;
para los números de coma flotante de tipo doble, la parte de la mantisa tiene 52 dígitos, y convertido a decimal es 2 ^ 52 = 4503599627370496, por lo que la precisión decimal es de solo 15 ~ 16 dígitos.
Por lo tanto, para el valor anterior 0.99999999f, es obvio que ha excedido el rango de precisión del tipo flotante flotante. datos puntuales y los problemas son inevitables.

Cómo resolver el problema de precisión
Entonces, ¿qué sucede si se trata de escenarios de alta precisión como el monto de la mercancía, el valor de transacción, el cálculo de la moneda, etc.?

Método 1: use una cadena o matriz para resolver un problema de varios dígitos

Aquellos que han resuelto problemas de algoritmos en el reclutamiento escolar deben saber que el uso de cadenas o matrices para representar grandes números es una idea típica de resolución de problemas.

Por ejemplo, las preguntas clásicas de la entrevista: escriba la suma, resta, multiplicación y otras operaciones de dos grandes números de dígitos arbitrarios.

En este momento, podemos usar cadenas o matrices para representar números tan grandes y luego simular manualmente el proceso de cálculo específico de acuerdo con las reglas de las cuatro operaciones aritméticas. En el medio, debemos considerar varios aspectos como llevar, pedir prestado, símbolo, etc., es realmente complicado, por lo que no entraré en detalles en este artículo.

Método 2: la clase de números grandes de Java es algo bueno

El JDK ya ha considerado la precisión de cálculo de los números de punto flotante para nosotros, por lo que proporciona una clase de números grandes dedicada a cálculos numéricos de alta precisión para facilitar nuestro uso.

Como se mencionó en el artículo anterior, "Te lo digo, recientemente ingresé a la barra de código fuente de Java", las clases de gran número de Java se encuentran en el paquete java.math:

 

Como puede ver, BigInteger y BigDecimal de uso común son herramientas poderosas para procesar cálculos numéricos de alta precisión.

BigDecimal num3 = new BigDecimal (Double.toString (1.0f));
BigDecimal num4 = new BigDecimal (Double.toString (0.99999999f));
System.out.println (num3 == num4); // imprime falso
 
BigDecimal num1 = nuevo BigDecimal (Double.toString (0.2));
BigDecimal num2 = new BigDecimal (Double.toString (0.7));
 
// agregar
System.out.println (num1.add (num2)); // imprimir: 0.9
 
// restar
System .out.println (num2.subtract (num1)); // Imprimir: 0.5
 
// Multiplicar
System.out.println (num1.multiply (num2)); // Imprimir: 0.14
 
// Dividir
System.out.println (num2 .divide (num1)); // Imprimir: 3.5
Por supuesto, la eficiencia computacional de números grandes como BigInteger y BigDecimal definitivamente no es tan eficiente como el tipo original, y el costo sigue siendo relativamente caro. evaluado de acuerdo con la escena real.

 

Supongo que te gusta

Origin blog.csdn.net/guodashen007/article/details/106409903
Recomendado
Clasificación