Estoy experimentando con la precisión de un valor doble en varios lenguajes de programación.
#include <stdio.h> int main() { for (double i = 0.0; i < 3; i = i + 0.1) { printf("%.17lf\n", i); } return 0; } #include <iostream> using namespace std; int main() { cout.precision(17); for (double i = 0.0; i < 3; i = i + 0.1) { cout << fixed << i << endl; } return 0; } i = 0.0 while i < 3: print(i) i = i + 0.1 public class Main { public static void main(String[] args) { for (double i = 0.0; i < 3; i = i + 0.1) { System.out.println(i); } } } 0.00000000000000000 0.10000000000000001 0.20000000000000001 0.30000000000000004 0.40000000000000002 0.50000000000000000 0.59999999999999998 0.69999999999999996 0.79999999999999993 0.89999999999999991 0.99999999999999989 1.09999999999999990 1.20000000000000000 1.30000000000000000 1.40000000000000010 1.50000000000000020 1.60000000000000030 1.70000000000000040 1.80000000000000050 1.90000000000000060 2.00000000000000040 2.10000000000000050 2.20000000000000060 2.30000000000000070 2.40000000000000080 2.50000000000000090 2.60000000000000100 2.70000000000000110 2.80000000000000120 2.90000000000000120 0.00000000000000000 0.10000000000000001 0.20000000000000001 0.30000000000000004 0.40000000000000002 0.50000000000000000 0.59999999999999998 0.69999999999999996 0.79999999999999993 0.89999999999999991 0.99999999999999989 1.09999999999999987 1.19999999999999996 1.30000000000000004 1.40000000000000013 1.50000000000000022 1.60000000000000031 1.70000000000000040 1.80000000000000049 1.90000000000000058 2.00000000000000044 2.10000000000000053 2.20000000000000062 2.30000000000000071 2.40000000000000080 2.50000000000000089 2.60000000000000098 2.70000000000000107 2.80000000000000115 2.90000000000000124 0.0 0.1 0.2 0.30000000000000004 0.4 0.5 0.6 0.7 0.7999999999999999 0.8999999999999999 0.9999999999999999 1.0999999999999999 1.2 1.3 1.4000000000000001 1.5000000000000002 1.6000000000000003 1.7000000000000004 1.8000000000000005 1.9000000000000006 2.0000000000000004 2.1000000000000005 2.2000000000000006 2.3000000000000007 2.400000000000001 2.500000000000001 2.600000000000001 2.700000000000001 2.800000000000001 2.9000000000000012 0.0 0.1 0.2 0.30000000000000004 0.4 0.5 0.6 0.7 0.7999999999999999 0.8999999999999999 0.9999999999999999 1.0999999999999999 1.2 1.3 1.4000000000000001 1.5000000000000002 1.6000000000000003 1.7000000000000004 1.8000000000000005 1.9000000000000006 2.0000000000000004 2.1000000000000005 2.2000000000000006 2.3000000000000007 2.400000000000001 2.500000000000001 2.600000000000001 2.700000000000001 2.800000000000001 2.9000000000000012 Sé que hay algunos errores en el tipo double sobre los cuales podemos obtener más información en blogs como Por qué nunca debe usar Float y Double para cálculos monetarios yLo que todo científico informático debe saber sobre la aritmética de punto flotante .
¡Pero estos errores no son aleatorios! Cada vez que los errores son los mismos, entonces mi pregunta es ¿por qué son diferentes para diferentes lenguajes de programación?
En segundo lugar, ¿por qué los errores de precisión en Java y Python son iguales? [La JVM de Java está escrita en C++ mientras que el intérprete de Python está escrito en C]
Pero, sorprendentemente, sus errores son los mismos, pero diferentes de los errores en C y C++. ¿Por qué está pasando esto?
Las diferencias que está viendo están en cómo imprime los datos, no en los datos en sí.
Como yo lo veo, tenemos dos problemas aquí. Una es que no está especificando constantemente la misma precisión cuando imprime los datos en cada idioma.
La segunda es que está imprimiendo los datos con 17 dígitos de precisión, pero al menos como se implementa normalmente (siendo el double un número de 64 bits con una significancia de 53 bits), un double realmente solo tiene alrededor de 15 dígitos decimales de precisión.
Entonces, mientras que (por ejemplo) C y C++ requieren que su resultado se redondee "correctamente", una vez que va más allá de los límites de precisión que se supone que admite, no pueden garantizar mucho sobre la producción de resultados verdaderamente idénticos en todos los casos posibles.
Pero eso solo afectará cómo se ve el resultado cuando lo imprima, no cómo se almacena internamente.
¡Pero estos errores no son aleatorios!
Correcto. Eso debería esperarse.
¿Por qué son diferentes para diferentes lenguajes de programación?
Porque ha formateado la salida de manera diferente.
¿Por qué los errores en Java y Python son los mismos?
Parecen tener el mismo formato predeterminado o lo suficientemente similar.
No sé sobre Python o Java, pero ni C ni C++ insisten en que la representación decimal impresa de un valor doble sea lo más precisa o concisa posible. Por lo tanto, comparar las representaciones decimales impresas no le dice todo sobre el valor real que se está imprimiendo. Dos valores pueden ser iguales en la representación binaria pero aún así se imprimen legítimamente como cadenas decimales diferentes en diferentes idiomas (o diferentes implementaciones del mismo idioma).
Por lo tanto, sus listas de valores impresos no le indican que esté sucediendo algo inusual.
Lo que debe hacer en su lugar es imprimir las representaciones binarias exactas de sus valores dobles.
Algunas lecturas útiles. https://www.exploringbinary.com/