Pregunta

Tengo una simple función de C #:

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

que calcula el número más alto, más bajo 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?

Actualizar Si puedo depurar la prueba que veo que los valores son iguales, hasta el punto decimal octavo lugar de la sexta, tal vez porque Math.Round introduce 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.

¿Fue útil?

Solución

Si omite todos los sufijos F (es decir, en lugar de -12.1 -12.1F) obtendrá la igualdad a algunos dígitos más. Sus constantes (y especialmente los valores esperados) son ahora flota debido a la F. Si usted está haciendo a propósito, por favor explicar.

Pero para el resto estoy de acuerdo con las otras respuestas en la comparación de los valores dobles o flotar por la igualdad, simplemente no es confiable.

Otros consejos

En realidad, una especie de deseo no habían aplicado el operador == para los flotadores y dobles. Casi siempre es lo peor que puede hacer para solicitar alguna vez si un doble o un flotador es igual a cualquier otro valor.

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

Si desea una precisión exacta a un número predefinido de decimales decimal en lugar de utilizar doble o aceptar un intervalo menor.

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 sólo punto flotante si desea que la interpretación de una máquina (binario) de los sistemas de numeración. No se puede representar de 10 centavos.

Si desea precisión, utilizar System.Decimal. Si quieres velocidad, utilizar System.Double (o System.Float). números de punto flotante no son números "infinita" de precisión, y por lo tanto la igualdad afirmando deben incluir una tolerancia. Siempre y cuando sus números tienen un número razonable de dígitos significativos, esto está bien.

  • Si usted está buscando para hacer matemáticas en números muy grandes y muy pequeños, no use flotador o doble.
  • Si necesita una precisión infinita, no utilice la o doble.
  • Si la agregación de un número muy grande de valores, no utilice la o doble (los errores se agravará a sí mismos).
  • Si necesita velocidad y el tamaño, el uso de flotador o doble.

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

Comprobar las respuestas a esta pregunta: ¿es seguro para comprobar los valores de punto flotante por la igualdad a 0?

En realidad, sólo echa para "dentro de la tolerancia de ..."

Los flotadores y los dobles no pueden almacenar con precisión todos los números. Esta es una limitación con el sistema IEEE de punto flotante. Con el fin de tener una precisión fiel es necesario utilizar una biblioteca matemática más avanzada.

Si usted no necesita una precisión más allá de cierto punto, entonces tal vez decimal va a funcionar mejor para usted. Cuenta con una precisión mayor que el doble.

Para la edición similar, termino 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));
}

A veces, el resultado es más preciso que se puede esperar de un estricto: FP IEEE 754. Esto se debe a HW utiliza más bits para el cálculo. Ver C # especificación y este artículo

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

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top