Consideremos el siguiente código C#:¿Por qué afecta el orden del redondeo al agregar múltiples dobles en C#
double result1 = 1.0 + 1.1 + 1.2;
double result2 = 1.2 + 1.0 + 1.1;
if (result1 == result2)
{
...
}
resultado1 debe ser siempre igual result2 ¿verdad? El caso es que no. result1 es 3.3 y result2 es 3.3000000000000003. La única diferencia es el orden de las constantes.
Sé que los dobles se implementan de tal forma que pueden ocurrir problemas de redondeo. Soy consciente de que puedo usar decimales si necesito precisión absoluta. O que puedo usar Math.Round() en mi sentencia if. Solo soy un nerd que quiere entender lo que hace el compilador de C#. ¿Alguien me puede decir?
Editar:
Gracias a todos los que hasta ahora sugieren leer sobre la aritmética de coma flotante y/o hablado sobre la inexactitud inherente de cómo la CPU maneja dobles. Pero siento que el impulso principal de mi pregunta aún no ha sido respondido. Cuál es mi culpa por no expresarlo correctamente. Déjame ponerlo de esta manera:
Romper el código anterior, esperaría que las siguientes operaciones para estar sucediendo:
double r1 = 1.1 + 1.2;
double r2 = 1.0 + r1
double r3 = 1.0 + 1.1
double r4 = 1.2 + r3
Vamos a suponer que cada una de las adiciones anteriores tenían un error de redondeo (e1 numerada. .e4). Entonces, r1 contiene el error de redondeo e1, r2 incluye los errores de redondeo e1 + e2, r3 contiene e3 y r4 contiene e3 + e4.
Ahora, no sé exactamente cómo ocurren los errores de redondeo pero esperaba que e1 + e2 fuera igual a e3 + e4. Está claro que no, pero eso me parece algo malo. Otra cosa es que cuando ejecuto el código anterior, no obtengo ningún error de redondeo. Eso es lo que me hace pensar que es el compilador C# que está haciendo algo raro en lugar de la CPU.
Sé que estoy pidiendo mucho y tal vez la mejor respuesta que alguien puede dar es ir a hacer un doctorado en diseño de CPU, pero pensé que podría preguntar.
Editar 2
En cuanto a la IL de mi ejemplo de código original, es claro que es el compilador no la CPU que está haciendo esto:
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
.maxstack 1
.locals init (
[0] float64 result1,
[1] float64 result2)
L_0000: nop
L_0001: ldc.r8 3.3
L_000a: stloc.0
L_000b: ldc.r8 3.3000000000000003
L_0014: stloc.1
L_0015: ret
}
El compilador es sumar los números de ¡yo!
+1 buen ejemplo concreto – bobince
El ejemplo es bueno, pero ha cambiado el orden de las operaciones en comparación con el código original. El OP hizo lo mismo en la primera edición. –