2009-04-15 10 views
8

¿Por qué el siguiente programa imprime lo que imprime?¿Por qué la aritmética de punto flotante en C# es imprecisa?

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

     Console.WriteLine(f1 > f2); 
    } 
} 

salida es

false 
+0

¿Por qué no lo pruebas solo? –

+10

Lo intenté, solo quiero saber por qué veo lo que veo. – Prankster

+1

[language-agnostic - ¿Las matemáticas de punto flotante están rotas?] (Http://stackoverflow.com/q/588004/995714) –

Respuesta

30

punto flotante única 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 la lectura What Every Computer Scientist Should Read About Floating Point

+0

+1 ¿Por qué, por qué veo después de presionar el botón Enviar que alguien ha publicado los enlaces? : P – dirkgently

+1

+1 http://msdn.microsoft.com/en-us/library/b1e65aza(VS.71).aspx Michael es correcto, C float solo tiene 7 dígitos de precisión, y 99.999999f tiene 8 dígitos. – James

+6

Esto no es específico de C#. Un flotador IEEE-754 de precisión simple solo tendrá 32 bits, lo que da alrededor de 7 dígitos decimales de precisión. Si quieres algo mejor que eso, usa un doble. – Wedge

5

Lo más importante es que esto no es sólo .Net: se trata de una limitación de la underlying system most every language will use to represent a float en la memoria. La precisión solo va tan lejos.

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

3

En este caso particular, se debe a 0,09 y 0,999999 no se puede representar con precisión exacta en binario (de manera similar, 1/3 no pueden representarse con precisión exacta en decimal). Por ejemplo, 0.111111111111111111101111 base 2 es 0.999998986721038818359375 base 10. Adición de 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.

La ejecución de este bit de código C++ en el depurador Xcode,

flotador 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.

en mi humilde opinión una muy buena explicación de punto flotante se encuentra en el capítulo 14 de Introducción a la Organización ordenador con x86-64 lenguaje ensamblador & 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 de 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 la -1.

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

La respuesta exacta es 1.84, o 1.84 veces 10 al 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 sumas de potencias inversas de dos". (Plantz, op. Cit.).

0,11100110 (binario) = 0,89843750 (decimal)

0,11100111 (binario) = 0,90234375 (decimal)

no hay 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.

programadores principiantes a menudo ven la aritmética de punto flotante más precisos que los entero. Es cierto que incluso agregar dos números 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 usa con dos enteros, el operador/en C/C++ hace que se pierda la parte fraccionaria . Sin embargo, ... las representaciones de coma flotante tienen su propio conjunto de imprecisiones . (Plantz, op. Cit.)

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

Cuestiones relacionadas