¿Por qué la aritmética de coma flotante en C # es imprecisa?

¿Por qué el siguiente progtwig imprime lo que imprime?

class Program { static void Main(string[] args) { float f1 = 0.09f*100f; float f2 = 0.09f*99.999999f; Console.WriteLine(f1 > f2); } } 

La salida es

 false 

El punto flotante solo tiene tantos dígitos de precisión. Si está viendo f1 == f2, es porque cualquier diferencia requiere más precisión que la que puede representar un flotador de 32 bits.

Recomiendo leer lo que todo informático debería leer sobre el punto flotante

Lo principal es que esto no es solo .Net: es una limitación del sistema subyacente que la mayoría de los idiomas usará para representar un flotador en la memoria. La precisión solo va tan lejos.

También puede divertirse con números relativamente simples, si se tiene en cuenta que ni siquiera es la base diez. 0.1, por ejemplo, es un decimal que se repite cuando está representado en binario.

En este caso particular, es porque .09 y .999999 no se pueden representar con precisión exacta en binario (de manera similar, 1/3 no se puede representar con precisión exacta en decimal). Por ejemplo, 0.111111111111111111101111 base 2 es 0.999998986721038818359375 base 10. Al agregar 1 al valor binario anterior, 0.11111111111111111111 base 2 es 0.99999904632568359375 base 10. No hay un valor binario para exactamente 0.999999. La precisión del punto flotante también está limitada por el espacio asignado para almacenar el exponente y la parte fraccional de la mantisa. Además, al igual que los tipos enteros, el punto flotante puede desbordar su rango, aunque su rango es mayor que los intervalos enteros.

Ejecutando este bit de código C ++ en el depurador Xcode,

flotar myFloat = 0.1;

muestra que myFloat obtiene el valor 0.100000001. Está desactivado por 0.000000001. No mucho, pero si el cálculo tiene varias operaciones aritméticas, la imprecisión puede ser complicada.

una buena explicación del punto flotante es en el Capítulo 14 de Introducción a la Organización de Computadores con x86-64 Assembly Language y GNU / Linux por Bob Plantz de la Universidad Estatal de California en Sonoma (retirado) http://bob.cs.sonoma.edu /getting_book.html . Lo siguiente está basado en ese capítulo.

El punto flotante es como la notación científica, donde un valor se almacena como un número mixto mayor o igual que 1.0 y menor que 2.0 (la mantisa), multiplicado por otro número hasta cierta potencia (el exponente). El punto flotante utiliza la base 2 en lugar de la base 10, pero en el modelo simple que Plantz da, usa la base 10 para mayor claridad. Imagine un sistema donde se usan dos posiciones de almacenamiento para la mantisa, una posición se utiliza para el signo del exponente * (0 representando + y 1 representando -), y una posición se usa para el exponente. Ahora agregue 0.93 y 0.91. La respuesta es 1.8, no 1.84.

9311 representa 0.93, o 9.3 veces 10 a -1.

9111 representa 0,91, o 9,1 veces 10 a -1.

La respuesta exacta es 1.84, o 1.84 veces 10 a 0, que sería 18400 si tuviéramos 5 posiciones, pero, teniendo solo cuatro posiciones, la respuesta es 1800, o 1.8 veces 10 al cero, o 1.8. Por supuesto, los tipos de datos de punto flotante pueden usar más de cuatro posiciones de almacenamiento, pero la cantidad de posiciones sigue siendo limitada.

No solo la precisión está limitada por el espacio, sino que “una representación exacta de valores fraccionarios en binario está limitada a sums de potencias inversas de dos” (Plantz, op cit.).

0.11100110 (binario) = 0.89843750 (decimal)

0.11100111 (binario) = 0.90234375 (decimal)

No hay una representación exacta de 0.9 decimal en binario. Incluso llevar la fracción a más lugares no funciona, ya que se llega a repetir 1100 para siempre a la derecha.

Los progtwigdores principiantes a menudo ven la aritmética de punto flotante como más precisa que el número entero. Es cierto que incluso agregar dos enteros muy grandes puede causar desbordamiento. La multiplicación hace que sea aún más probable que el resultado sea muy grande y, por lo tanto, desborde. Y cuando se utiliza con dos enteros, el operador / en C / C ++ hace que se pierda la parte fraccionaria. Sin embargo, … las representaciones de punto flotante tienen su propio conjunto de imprecisiones. (Plantz, op cit.)

* En punto flotante, se representan tanto el signo del número como el signo del exponente.