2009-02-19 14 views
15

que tienen una simple función de C#:problemas de doble precisión en .NET

public static double Floor(double value, double step) 
{ 
    return Math.Floor(value/step) * step; 
} 

que calcula el número mayor, menor o igual que el "valor", que es múltiplo de "paso". Pero carece de precisión, como se ve en las siguientes pruebas:

[TestMethod()] 
public void FloorTest() 
{ 
    int decimals = 6; 
    double value = 5F; 
    double step = 2F; 
    double expected = 4F; 
    double actual = Class.Floor(value, step); 
    Assert.AreEqual(expected, actual); 
    value = -11.5F; 
    step = 1.1F; 
    expected = -12.1F; 
    actual = Class.Floor(value, step); 
    Assert.AreEqual(Math.Round(expected, decimals),Math.Round(actual, decimals)); 
    Assert.AreEqual(expected, actual); 
} 

El primer y el segundo afirma están bien, pero la tercera falla, porque el resultado es solamente igual hasta que el sexto decimal. ¿Porqué es eso? ¿Hay alguna manera de corregir esto?

Actualización Si depuro la prueba, veo que los valores son iguales hasta el octavo lugar decimal en lugar de la 6ª, tal vez porque Math.Round presenta cierta imprecisión.

Nota En mi código de prueba que escribió el sufijo "F" (explícita constante float), donde me refería a "D" (doble), por lo que si puedo cambiar lo que puedo tener más precisión.

Respuesta

5

Si omite todas las postfixes F (es decir, -12.1 en lugar de -12.1F) obtendrá igualdad en algunos dígitos más. Sus constantes (y especialmente los valores esperados) ahora son flotantes debido al F. Si lo haces a propósito, explícalo.

Pero por lo demás estoy de acuerdo con las otras respuestas al comparar los valores dobles o float para la igualdad, simplemente no es confiable.

+0

pero la F mayúscula significa doble, no flotar, ¿verdad? es la f minúscula que significa flotar. –

+0

No, acabo de comprobar: flotante x = 1.0; da un error, float x = 1.0F; está bien La F no distingue entre mayúsculas y minúsculas. –

+1

Y lo busqué en Ecmea334: 1.0D para el doble, 1.0M para el decimal. –

8

flotante aritmética de punto en los ordenadores no son exactas Ciencia :).

Si desea precisión exacta para un número predefinido de decimales, utilice Decimal en lugar de doble o acepte un intervalo menor.

+5

Es una ciencia exacta dentro del número definido por el IEEE de dígitos significativos. –

+1

Para reforzar lo anterior: los números de punto flotante _ son_ exactos. Es posible que el número que desea no se pueda representar como un número de punto flotante IEEE, lo que significa que debe aliar el número al más cercano, lo que genera un error, pero eso no significa que los números que pueda representar tengan un error en ellos. – codekaizen

+2

Además, los decimales pueden sufrir el mismo problema que los dobles, ya que también son puntos flotantes. Pueden encontrar el mismo problema de representación, pero es mucho menos inesperado, ya que tienen una base de 10, frente a una base de 2, y estamos acostumbrados a tratar problemas de representación en la base 10 (por ejemplo, 1/3 es 0.333333). en la base 10). – codekaizen

1

flotantes y dobles no pueden almacenar con precisión todos los números. Esta es una limitación con el sistema de coma flotante IEEE. Para tener una precisión fiel necesita usar una biblioteca matemática más avanzada.

Si no necesita precisión más allá de cierto punto, entonces tal vez el decimal funcionará mejor para usted. Tiene una precisión más alta que el doble.

4

http://en.wikipedia.org/wiki/Floating_point#Accuracy_problems

Por ejemplo, la no representabilidad de 0,1 y 0,01 (en binario) significa que el resultado de intentar cuadrado 0,1 no es ni 0.01 ni el número representable más cercano a él.

Utilice solo el punto flotante si desea la interpretación de una máquina (binaria) de sistemas numéricos. No puedes representar 10 centavos.

4

Si quiere precisión, use System.Decimal. Si desea velocidad, use System.Double (o System.Float). Los números de coma flotante no son números de "precisión infinita" y, por lo tanto, afirmar la igualdad debe incluir una tolerancia. Siempre que sus números tengan una cantidad razonable de dígitos significativos, esto está bien.

  • Si está buscando hacer cálculos matemáticos en números muy grandes Y muy pequeños, no use float o double.
  • Si necesita una precisión infinita, no use flotador o doble.
  • Si está agregando una gran cantidad de valores, no use float o double (los errores se combinarán).
  • Si necesita velocidad y tamaño, use flotante o doble.

Ver this respuesta (también por mí) para un análisis detallado de cómo la precisión afecta el resultado de sus operaciones matemáticas.

+0

No hay 'precisión infinita'. El problema con float/double es que son precisos para una cantidad de dígitos binarios y no para un número de dígitos decimales. – configurator

+2

Existe la precisión infinita.Un entero es un tipo infinitamente preciso. No pierde precisión durante las operaciones matemáticas. Es posible implementar un tipo de decimal infinitamente preciso (aunque muy ineficiente), pero no está "listo para usar" en .Net. –

+0

Michael, tu comentario es tonto. Definido de esta manera, para que se pueda decir que los enteros tienen "precisión infinita" solo porque no cambian durante una operación matemática, entonces cada número tiene "Precisión Infinita", (flotantes, dobles y decimales no cambian durante las operaciones matemáticas tampoco) . Definido de esta manera, el concepto pierde todo significado. Diablos, incluso un solo signo con solo dos valores, (Positivo o Negativo) tiene "Precisión Infinita", porque, de acuerdo con su definición, no pierde precisión durante cualquier operación matemática en la que se usa. –

0

Para el problema similar, que terminan con la siguiente aplicación que parece el éxito mayor parte de mi caso de prueba (hasta 5 dígitos de precisión):

public static double roundValue(double rawValue, double valueTick) 
{ 
    if (valueTick <= 0.0) return 0.0; 

    Decimal val = new Decimal(rawValue); 
    Decimal step = new Decimal(valueTick); 
    Decimal modulo = Decimal.Round(Decimal.Divide(val,step)); 

    return Decimal.ToDouble(Decimal.Multiply(modulo, step)); 
} 
8

De hecho, me especie de deseo no habían aplicado el operador == para flotantes y dobles. Casi siempre es algo incorrecto preguntar si alguna vez un doble o un flotador es igual a cualquier otro valor.

+1

¡SÍ! ¡SÍ! ¡SÍ! Lo he estado diciendo desde hace un tiempo. Es como el problema entero de 0.999 ... = 1.0. (1.0 - 0.000 ... = 1.0). Los puntos flotantes son un animal completamente diferente a los enteros. – Josh

0

A veces el resultado es más preciso de lo que cabría esperar de strict: FP IEEE 754. Esto se debe a que HW usa más bits para el cálculo. Ver C# specification y this article

Java tiene la palabra clave strictfp y C++ tiene los modificadores del compilador. Echo de menos esa opción en .NET

Cuestiones relacionadas