¿La doble multiplicación está rota en .NET?

Si ejecuto la siguiente expresión en C #:

double i = 10*0.69; 

6.8999999999999995 : 6.8999999999999995 . ¿Por qué?

Entiendo que números como 1/3 pueden ser difíciles de representar en formato binario ya que tiene infinitos lugares decimales recurrentes, pero este no es el caso para 0.69. Y 0.69 puede representarse fácilmente en binario, un número binario para 69 y otro para denotar la posición del lugar decimal.

¿Cómo trabajo alrededor de esto? Use el tipo de decimal ?

Porque ha entendido mal la aritmética de coma flotante y cómo se almacenan los datos.

De hecho, su código no está realizando ninguna aritmética en el momento de la ejecución en este caso particular: el comstackdor lo habrá hecho y luego guardado una constante en el ejecutable generado. Sin embargo, no puede almacenar un valor exacto de 6.9, porque ese valor no se puede representar con precisión en formato de coma flotante, al igual que 1/3 no se puede almacenar con precisión en una representación decimal finita.

Vea si este artículo lo ayuda.

¿Por qué el marco de trabajo no funciona así y me oculta este problema y me da la respuesta correcta, 0,69?

Deje de comportarse como un administrador dilbert, y acepte que las computadoras, aunque sean geniales e increíbles, tengan límites. En su caso específico, no solo “oculta” el problema, porque específicamente le ha dicho que no lo haga. El idioma (la computadora) proporciona alternativas al formato que usted no eligió. Eliges el doble, que tiene ciertas ventajas sobre el decimal y ciertas desventajas. Ahora, sabiendo la respuesta, te molesta que los inconvenientes no desaparezcan mágicamente.

Como progtwigdor, usted es responsable de ocultar esta desventaja de los gerentes, y hay muchas maneras de hacerlo. Sin embargo, los creadores de C # tienen la responsabilidad de hacer que el punto flotante funcione correctamente, y corregir el punto flotante ocasionalmente dará como resultado una matemática incorrecta.

Lo mismo ocurrirá con cualquier otro método de almacenamiento numérico, ya que no tenemos bits infinitos. Nuestro trabajo como progtwigdores es trabajar con recursos limitados para que sucedan cosas interesantes. Te consiguieron el 90% del camino hasta allí, solo lleva la antorcha a casa.

Y 0.69 puede representarse fácilmente en binario, un número binario para 69 y otro para denotar la posición del lugar decimal .

Creo que este es un error común: estás pensando en los números flotantes como si fueran base-10 (es decir, decimales, de ahí mi énfasis).

Entonces, estás pensando que hay dos partes de números enteros para este doble: 69 y divide entre 100 para obtener el lugar decimal para moverse, lo que también podría expressse como:
69 x 10 a la potencia de -2 .

Sin embargo, los flotantes almacenan la ‘posición del punto’ como base-2 .

Su flotador en realidad se almacena como:
68999999999999995 x 2 a la potencia de un gran número negativo

Esto no es un gran problema una vez que esté acostumbrado: la mayoría de la gente sabe y espera que 1/3 no se pueda express con precisión como un decimal o porcentaje. Es solo que las fracciones que no se pueden express en base-2 son diferentes.

pero ¿por qué el marco no funciona en este sentido y me oculta este problema y me da la respuesta correcta, 0,69?

Porque le dijo que use un punto flotante binario , y la solución es usar punto flotante decimal , por lo que está sugiriendo que el marco debe ignorar el tipo especificado y usar decimal , lo que es mucho más lento porque no se implementa directamente en hardware.

Una solución más eficiente es no generar el valor completo de la representación y especificar explícitamente la precisión requerida por su salida. Si formatea la salida con dos decimales, verá el resultado que espera. Sin embargo, si se trata de una aplicación financiera, el decimal es exactamente lo que debe usar: ha visto que Superman III (y Office Space) no lo ha hecho;)

Tenga en cuenta que es toda una aproximación finita de un rango infinito, es simplemente que el decimal y el doble usan un conjunto diferente de aproximaciones. La ventaja del decimal es que produce las mismas aproximaciones que tendrías si estuvieras realizando el cálculo tú mismo. Por ejemplo, si calcula 1/3, eventualmente dejaría de escribir 3 cuando era ‘suficientemente bueno’.

Por la misma razón que 1/3 en sistemas decimales sale como 0.3333333333333333333333333333333333333333333 y no la fracción exacta, que es infinitamente larga.

Para evitarlo (por ejemplo, para mostrar en pantalla) intente esto:

 double i = (double) Decimal.Multiply(10, (Decimal) 0.69); 

Todo el mundo parece haber respondido a su primera pregunta, pero ignoró la segunda parte.