2011-01-12 41 views
12

Podría alguien aquí, por favor ayúdeme a comprender cómo determinar cuándo las limitaciones de punto flotante causarán errores en sus cálculos. Por ejemplo, el siguiente código.Comprensión de los problemas de punto flotante

CalculateTotalTax = function (TaxRate, TaxFreePrice) { 
    return ((parseFloat(TaxFreePrice)/100) * parseFloat(TaxRate)).toFixed(4); 
}; 

No he podido ingresar dos valores que hayan causado un resultado incorrecto para este método. Si elimino el toFixed (4) puedo ver de hecho dónde comienzan a perder precisión los cálculos (alrededor del sexto lugar decimal). Habiendo dicho eso, mi comprensión de las carrozas es que incluso los números pequeños a veces no pueden ser representados o he malentendido y pueden representarse con precisión 4 lugares decimales (por ejemplo).

MSDN explica flotadores como such ...

Esto significa que no pueden mantener una representación exacta de cualquier cantidad que es no una fracción binaria (de la forma k/ (2^n), donde k y n son números enteros)

Ahora supongo que esto se aplica a todas las carrozas (incluidas las utilizadas en javascript).

Fundamentalmente mi pregunta se reduce a esto. ¿Cómo se puede determinar si algún método específico será vulnerable a los errores en operaciones de punto flotante, con qué precisión se materializarán esos errores y qué entradas se necesitarán para producir esos errores?

Espero que lo que estoy pidiendo tenga sentido.

+0

Quizás esto se relaciona con este tema: http://en.wikipedia.org/wiki/Machine_epsilon –

+0

* Tony the Pony *, también conocido como [Jon Skeet] (http://stackoverflow.com/users/22656/jon-skeet), tiene una [excelente explicación] (http://codeblog.jonskeet.uk/2009/11/02/omg-ponies-aka-humanity-epic-fail/). Legible, claro, divertido sin ser irritante, y no tan pesado como [algunos enlaces] (http://docs.sun.com/source/806-3568/ncg_goldberg.html) que a menudo se publican sobre este tema. – MarkJ

Respuesta

10

de inicio mediante la lectura de Lo que todo científico debe saber sobre el ordenador de punto flotante: http://docs.sun.com/source/806-3568/ncg_goldberg.html

Respuesta corta: flotadores de doble precisión (que son el valor por defecto en JavaScript) tiene cerca de 16 dígitos decimales de precisión. El redondeo puede variar de una plataforma a otra. Si es absolutamente esencial que obtenga la respuesta correcta de manera consistente, debe hacer usted mismo la aritmética racional (no es necesario que sea difícil, para la moneda, tal vez puede multiplicar por 100 para almacenar la cantidad de centavos como un número entero).

Pero si es suficiente para obtener la respuesta con un alto grado de precisión, los flotadores deberían ser lo suficientemente buenos, especialmente la precisión doble.

4

hay dos cosas importantes que usted debe ahora cuando se trata de flotadores:

1- Usted debe ser consciente de machine epsilon. Para saber cuánta precisión tienes.

2- Usted no debería asumir if two values are equal in base 10, they are equal in base 2 in a machine with precision limit.

if ((6.0/10.0)/3.0 != .2) { 
     cout << "gotcha" << endl; 
} 

Número 2 puede ser lo suficientemente convincente como para que se evite la comparación de números de punto flotante por la igualdad, en lugar de un umbral y mayor que o menos que los operadores se pueden utilizar para la comparación

+1

El número 2 no implica que "nunca se deben comparar los números de coma flotante para la igualdad"; después de todo, sería tan fácil seguir que nunca debería comparar números decimales por igualdad. Significa que uno no debe escribir código que no se entiende. Tómese el tiempo para aprender cómo funciona el punto flotante. –

+0

Como dijiste, uno debería tomarse un tiempo para aprender el punto flotante; algo así como (0.1 == 1/10) parece tan inocente que nadie probablemente piense que está causando un error a menos que lo haya leído antes. Incluso hablando de conceptos básicos de matemáticas, no mucha gente sabe que 10 = 9.999 ... o cómo convertir un decimal a binario. –

+1

La razón '.1! = 1/10' es porque' 1/10 == 0'. Estás usando la división de enteros. –

1

No, el número de lugares decimales no tiene nada que ver con lo que se puede representar.

Pruebe .1 * 3, o 162.295/10, o 24.0 + 47.98. Esos fallan en JS. Pero, 24.0 * 47.98 no falla.

Para responder a sus tres preguntas, cualquier operación con precisión es potencialmente vulnerable. Si una respuesta dada será o no será una pregunta que no sé cómo responder, pero tengo la corazonada de que hay una serie de factores. 1) Qué tan cerca está la respuesta real de la fracción binaria más cercana. 2) La precisión en el motor que realiza el cálculo. 3) El método utilizado para realizar el cálculo (por ejemplo, multiplicar por desplazamiento de bit puede dar resultados diferentes a la multiplicación por adición repetida)

2

Las otras respuestas indican buenos recursos para comprender este problema. Si realmente usa valores monetarios en su código (como en su ejemplo), debería preferir los tipos Decimal (System.Decimal en .Net). Esto evitará que algunos de los problemas de redondeo utilicen flotantes y combinen mejor el dominio.

Cuestiones relacionadas