20 Stimmen

Zwei Schwimmer vergleichen

#include <stdbool.h>

bool Equality(double a, double b, double epsilon)
{
  if (fabs(a-b) < epsilon) return true;
  return false;
}

Ich habe diese Methode ausprobiert, um zwei Doppelgänger zu vergleichen, aber ich bekomme immer Probleme, da ich nicht weiß, wie ich die epsilon Ich möchte eigentlich kleine Zahlen (6 6 Stellen nach dem Komma) vergleichen, wie 0.000001 . Ich habe es mit einigen Zahlen versucht, manchmal bekomme ich 0.000001 != 0.000001 und manchmal 0.000001 == 0.000002 Gibt es eine andere Methode als den Vergleich mit dem Epsilon?

Mein Ziel ist es, zwei Doppelwerte zu vergleichen (die in meinem Fall die Zeit darstellen). Die Variable t, die die Zeit in Millisekunden darstellt, ist ein Double. Sie wird von einer anderen Funktion um 0,000001, dann um 0,000002 usw. erhöht. Jedes Mal, wenn sich t ändert, möchte ich prüfen, ob sie gleich einer anderen Variablen vom Typ double tt ist, falls tt == t ist, muss ich einige Anweisungen ausführen.
Vielen Dank für Ihre Hilfe

23voto

Roddy Punkte 64661

Siehe hier: http://floating-point-gui.de/errors/comparison/

Aufgrund von Rundungsfehlern werden die meisten Gleitkommazahlen am Ende als leicht ungenau. Solange diese Ungenauigkeit gering bleibt, kann sie normalerweise ignoriert werden. Das bedeutet aber auch, dass die zu erwartenden Zahlen gleich sind (z.B. bei der Berechnung desselben Ergebnisses durch verschiedene korrekte Methoden) oft leicht voneinander abweichen und ein einfacher Gleichheitstest fehlschlägt .

Und, natürlich, Was jeder Informatiker über Fließkommaarithmetik wissen sollte

3voto

unwind Punkte 377331

Erstens: Es hat keinen Sinn, einen booleschen Wert zu berechnen (mit dem < Operator) und dann in einen anderen Booleschen Wert verpackt. Schreiben Sie es einfach so:

bool Equality(float a, float b, float epsilon)
{
  return fabs(a - b) < epsilon;
}

Zweitens ist es möglich, dass Ihr Epsilon selbst nicht gut repräsentiert ist als float und sieht daher nicht so aus, wie Sie es erwarten. Versuchen Sie es mit einer negativen Potenz von 2, wie z. B. 1/1048576.

1voto

rebs01 Punkte 11

Stattdessen können Sie auch zwei Ganzzahlen vergleichen. Multiplizieren Sie einfach Ihre beiden Fließkommazahlen mit der gewünschten Genauigkeit und wandeln Sie sie in Ganzzahlen um. Achten Sie darauf, dass Sie korrekt auf- und abrunden. So sieht das Ganze aus:

BOOL floatcmp(float float1, float float2, unsigned int precision){
   int int1, int2;

   if (float1 > 0)
      int1 = (int)(float1 * precision + .5);
   else
      int1 = (int)(float1 * precision - .5);

   if (float2 > 0)
      int2 = (int)(float2 * precision + .5);
   else
      int2 = (int)(float2 * precision - .5);

   return (int1 == int2);
}

1voto

Chameleon Punkte 1522

Denken Sie daran, dass, wenn float a = +2^(254-127) * 1.___22 zeros___1 et float b = +2^(254-127) * 1.___23 zeros___ dann erwarten wir abs(a-b) < epsilon sondern stattdessen a - b = +2^(254-127-23) * 1.___23 zeros___ = 20282409603651670423947251286000 was viel größer ist als Epsilon...

1voto

Chameleon Punkte 1522

Ich schreibe und teste diesen Code. Es scheint zu funktionieren.

public static boolean equal(double a, double b) {
    final long fm = 0xFFFFFFFFFFFFFL;       // fraction mask
    final long sm = 0x8000000000000000L;    // sign mask
    final long cm = 0x8000000000000L;       // most significant decimal bit mask
    long c = Double.doubleToLongBits(a), d = Double.doubleToLongBits(b);        
    int ea = (int) (c >> 52 & 2047), eb = (int) (d >> 52 & 2047);
    if (ea == 2047 && (c & fm) != 0 || eb == 2047 && (d & fm) != 0) return false;   // NaN 
    if (c == d) return true;                            // identical - fast check
    if (ea == 0 && eb == 0) return true;                // ±0 or subnormals
    if ((c & sm) != (d & sm)) return false;             // different signs
    if (abs(ea - eb) > 1) return false;                 // b > 2*a or a > 2*b
    d <<= 12; c <<= 12;
    if (ea < eb) c = c >> 1 | sm;
    else if (ea > eb) d = d >> 1 | sm;
    c -= d;
    return c < 65536 && c > -65536;     // don't use abs(), because:
    // There is a posibility c=0x8000000000000000 which cannot be converted to positive
}
public static boolean zero(double a) { return (Double.doubleToLongBits(a) >> 52 & 2047) < 3; }
  • wenn eine der Zahlen NaN ist, sind die Zahlen ungleich.
  • Wenn 2 Zahlen identisch sind, sind sie gleich. Dies ist eine schnelle erste Überprüfung.
  • Wenn beide Zahlen einen Wert von +0, -0 oder subnormal haben, sind die Zahlen gleich.
  • Wenn Zahlen unterschiedliche Vorzeichen haben, sind die Zahlen ungleich. Dies scheint ein falscher Ansatz zu sein, wenn beide Zahlen fast 0 sind (aber nicht ±0 oder Subnormalzahlen) und unterschiedliche Vorzeichen haben. Aber was ist, wenn man diese Zahlen mit einer anderen multipliziert? Das eine Ergebnis wird negativ sein und das andere positiv. Wir sind also streng und das ist richtig.
  • Wenn die Exponenten eine Differenz von 2 oder mehr haben, sind die Zahlen ungleich, denn eine Zahl ist mindestens 2 Mal größer als die andere.
  • wenn die Exponenten genau die Differenz 1 haben, verschiebe den Bruch von einer der Zahlen richtig.
  • Wenn die Differenz von 2 Brüchen klein ist, sind die Zahlen gleich.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X