2010-06-13 18 views
7

Duplicar posible:
Dealing with accuracy problems in floating-point numbers¿Por qué no puedo multiplicar un flotador?

yo estaba bastante sorprendido por eso traté de multiplicar un flotador en C (con GCC 3.2) y que no hizo lo que yo esperaba .. Como una muestra:

int main() { 
    float nb = 3.11f; 
    nb *= 10; 
    printf("%f\n", nb); 
} 

muestra: 31,099998

Tengo curiosidad acerca de cómo se implementan las carrozas y por qué produce este comportamiento inesperado.

+4

La respuesta corta es que la mantisa se almacena como un valor binario, no como un valor decimal. Entonces, los únicos números representados exactamente son aquellos que pueden descomponerse en poderes de dos (es decir, 1/2, 1/4, 1/8, etc.) Para la historia completa, intente http://docs.sun.com /source/806-3568/ncg_goldberg.html –

+7

Duplicado de MUCHAS preguntas. Vea la etiqueta "precisión flotante". – dan04

+1

posible duplicado de [Manejo de problemas de precisión en números de coma flotante] (http://stackoverflow.com/questions/590822/decision-with-accuracy-problems-in-floating-point-numbers). [¿Las matemáticas de JavaScript están rotas?] (Http://stackoverflow.com/questions/588004/is-javascripts-math-broken) es bueno también –

Respuesta

14

En primer lugar, usted puede multiplicar flota. El problema que tienes no es la multiplicación en sí, sino el número original que has usado. La multiplicación puede perder algo de precisión, pero aquí el número original que has multiplicado comenzó con la precisión perdida.

Esto es en realidad un comportamiento esperado. float s se implementan usando representación binaria lo que significa que no pueden representar valores decimales con precisión.

Consulte MSDN para obtener más información.

También puede ver en the description of float que tiene una precisión de 6-7 dígitos significativos. En su ejemplo, si redondea 31.099998 a 7 dígitos significativos obtendrá 31.1 por lo que sigue funcionando como se espera aquí.

double tipo, por supuesto, sería más preciso, pero aún tiene un error de redondeo debido a su representación binaria, mientras que el número que escribió es decimal.

Si desea una precisión completa para los números decimales, debe usar un tipo de decimal. Este tipo existe en idiomas como C#. http://msdn.microsoft.com/en-us/library/system.decimal.aspx

También puede usar la representación de números racionales. Usando dos enteros que le darán una precisión completa, siempre y cuando pueda representar el número como una división de dos enteros.

+0

Gran explicación, gracias ! – Dpp

+0

lea también lea también http://en.wikipedia.org/wiki/IEEE_754-2008, http://grouper.ieee.org/groups/754/ y http://ieeexplore.ieee.org/xpl/freeabs_all. jsp? arnumber = 4610935 (si puede ser un suscriptor ...) – ShinTakezou

5

Esto está funcionando como se esperaba. Las computadoras tienen una precisión finita, porque están tratando de calcular valores de punto flotante a partir de números enteros. Esto conduce a imprecisiones de coma flotante.

La página Floating point wikipedia entra en mucho más detalle en la representación y problemas de precisión resultantes de lo que podía aquí :)

interesante en el mundo real nota lateral: this es en parte por qué una gran cantidad de cálculos de dinero se realizan usando números enteros (centavos): ¡no permita que la computadora pierda dinero con falta de precisión! ¡Quiero mi $ 0.00001!

+0

* "... ¡no permita que la computadora pierda dinero con errores de redondeo! Quiero mi $ 0.00001" *. Los cálculos enteros no previenen los errores de redondeo. (¿Cuál es la representación entera exacta de '$ 10/3' ???) Simplemente hacen que sean más fáciles de aceptar para las personas sin conocimientos informáticos. –

+0

C: Gracias, aclarado. – Stephen

+2

Es cierto que no se puede hacer una * división * exacta sin una aritmética racional. Pero almacenar cantidades en centavos proporciona * representación * exacta, así como la suma, resta y multiplicación exactas por enteros. – dan04

1

De Wikipedia article:

El hecho de que los números de coma flotante no pueden representar con precisión todos números reales, y que de punto flotante operaciones no se pueden representar con precisión verdaderas operaciones aritméticas, conduce a muchas situaciones sorprendentes .Esto es relacionado con la precisión finita con cuyas computadoras generalmente representan números .

2

En las comunidades de Python que vemos a menudo la gente sorprendido por esto, por lo que no se hayan probado-y-depurarse FAQs y tutorial sections sobre el tema (por supuesto que están formuladas en términos de Python, no C, pero como los delegados de Python flotan la aritmética al C subyacente y al hardware de todos modos, todas las descripciones de la mecánica de float se siguen aplicando).

No es culpa de la multiplicación, por supuesto, elimine la instrucción donde se multiplica nb y verá problemas similares de todos modos.

0

Los puntos flotantes no son precisos porque usan la base 2 (porque es binaria: 0 o 1) en lugar de la base 10. Y la conversión de la base 2 a la base 10, como han indicado anteriormente, causará problemas de precisión de redondeo.

+1

Cualquier base que elija le dará problemas de precisión de redondeo. Es solo que estamos acostumbrados a los asociados con la base 10 (rápido: ¿cuál es el valor preciso de 1 dividido por 3?) Y no a los asociados con la base 2. –

+0

@JUST MI OPINIÓN correcta: Sí, todos darían valores imprecisos . Y también lo hace la base 2, y como las carrozas usan la base 2, es por eso que la estoy usando en mi respuesta.Además, cuando conviertes de una base a otra base, pierdes precisión. –

+0

Es importante tener en cuenta que los problemas de precisión de redondeo existen en formato decimal. Por ejemplo, en Python, Decimal (1)/3 * 3 == Decimal ('0.9999999999999999999999999999') y Decimal (2) .sqrt() ** 2 == Decimal ('1.999999999999999999999999999'). – dan04

4

El número 3.11 no se puede representar en binario. Lo más cercano que puede obtener con 24 bits significativos es 11,0001110000101000111101, que funciona en 3,1099998950958251953125 en decimal.

Si se supone que su número 3.11 representa una cantidad monetaria, entonces necesita usar una representación decimal.

Cuestiones relacionadas