Frage

Ich habe eine einfache C # Funktion:

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

Dies berechnet die höhere Zahl, kleiner als oder gleich „Wert“, das heißt Mehrfach von „Schritt“. Aber es fehlt Präzision, wie sie in den folgenden Tests gesehen:

[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);
}

Die erste und die zweite behauptet, sind in Ordnung, aber die dritten fehlschlägt, weil das Ergebnis bis zum 6. Nachkommastelle nur gleich ist. Warum ist das so? Gibt es eine Möglichkeit, dies zu korrigieren?

Aktualisieren Wenn ich den Test Ich sehe debuggen, dass die Werte bis zum 8. Dezimalstelle gleich sind anstelle des sechsten, vielleicht weil Math.Round stellt einige Unschärfen.

Hinweis In meinem Test Code, den ich das "F" Suffix schrieb (explizite float konstant), wo ich "D" (double) gemeint, also wenn ich, dass ich mehr Präzision ändern.

War es hilfreich?

Lösung

Wenn Sie alle F Postfix auslassen (dh -12.1 statt -12.1F) Sie werden mehr Gleichheit auf wenige Stellen bekommen. Ihre Konstanten (und vor allem die erwarteten Werte) sind jetzt wegen der F schwimmt. Wenn Sie das absichtlich tun, dann bitte erklären.

Aber für den Rest i mit den anderen Antworten stimmen auf doppelte oder Float-Werte für die Gleichstellung zu vergleichen, es ist einfach nicht zuverlässig.

Andere Tipps

ich eigentlich irgendwie wollen sie den Operator == für Schwimmer und verdoppelt werden nicht umgesetzt hatte. Es ist fast immer das Falsche zu tun jemals zu fragen, ob ein Doppel- oder ein Schwimmer gleich auf einen anderen Wert.

Gleitkommaarithmetik auf Computern sind nicht Wissenschaft :) Exact.

Wenn Sie genaue Präzision auf eine vorgegebene Anzahl von Dezimalstellen wollen verwenden Dezimal statt Doppel oder ein kleiners Intervall akzeptieren.

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

  

Zum Beispiel kann die nicht-Darstellbarkeit von 0,1 und 0,01 (binär) bedeutet, dass das Ergebnis des Versuchs, auf 0,1 Quadrat weder 0,01 noch die darstellbare Zahl am nächsten zu.

Nur Floating-Point verwenden, wenn Sie eine Maschine Interpretation (binär) von Zahlensystemen wollen. Sie können nicht mehr als 10 Cent darstellen.

Wenn Sie Präzision möchten, verwenden Sie System.Decimal. Wenn Sie Geschwindigkeit wollen, verwenden Sie System.Double (oder System.Float). Gleitkommazahlen sind nicht „unendlich Präzision“ Zahlen und daher behaupten Gleichheit muss eine Toleranz enthalten. Solange Ihre Zahlen eine angemessene Anzahl von signifikanten Stellen haben, ist dies in Ordnung.

  • Wenn Sie schauen, Mathe auf sehr große und sehr kleine Zahlen zu tun, verwenden Sie nicht float oder double.
  • Wenn Sie unendliche Genauigkeit benötigen, verwenden Sie nicht float oder double.
  • Wenn Sie eine sehr große Anzahl von Werten werden aggregiert, verwenden Sie nicht float oder double (die Fehler werden sich Verbindung).
  • Wenn Sie Geschwindigkeit und Größe, Verwendung float oder double.

Siehe diese Antwort (auch von mir) für eine detaillierte Analyse der Präzision das Ergebnis Ihrer mathematischen Operationen betrifft.

Überprüfen Sie die Antworten auf diese Frage: Ist es sicher auf 0 Gleitkommazahlen für die Gleichstellung zu überprüfen?

Wirklich, nur überprüfen "innerhalb der Toleranz von ..."

floats und doubles nicht genau alle Nummern speichern. Dies ist eine Einschränkung mit dem IEEE-Gleitkomma-System. Um treu Präzision zu haben, benötigen Sie eine erweiterte Mathematik-Bibliothek verwenden.

Wenn Sie keine Genauigkeit über einen bestimmten Punkt benötigen, dann vielleicht wird dezimal besser für Sie arbeiten. Es hat eine höhere Präzision als das Doppelte.

Für das ähnliche Problem, ich am Ende der folgende Implementierung verwendet, die zum Erfolg der meisten meines Testfalls scheint (bis zu 5-stellige Genauigkeit):

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));
}

Manchmal ist das Ergebnis präziser als Sie von den strengen erwarten: FP IEEE 754. Das ist, weil HW mehr Bits für die Berechnung verwendet. Siehe C # Spezifikation und dieser Artikel

Java hat strictfp Schlüsselwort und C ++ haben Compiler-Switches. Ich vermisse diese Option in .NET

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top