wake-up-neo.com

Funktion, um zu bestimmen, ob zwei Zahlen nahezu gleich sind, wenn sie auf n signifikante Dezimalstellen gerundet werden

Ich wurde gebeten, eine von einem Drittanbieter bereitgestellte Bibliothek zu testen. Es ist bekannt, dass die Bibliothek auf n signifikante Zahlen genau ist. Geringfügige Fehler können ignoriert werden. Ich möchte eine Funktion schreiben, die mir hilft, die Ergebnisse zu vergleichen:

def nearlyequal( a, b, sigfig=5 ):

Der Zweck dieser Funktion besteht darin, zu bestimmen, ob zwei Gleitkommazahlen (a und b) ungefähr gleich sind. Die Funktion gibt True zurück, wenn a == b (exakte Übereinstimmung) oder wenn a und b den gleichen Wert haben, wenn sie auf sigfig Hochstellen gerundet sind, wenn sie in Dezimalzahl geschrieben sind.

Kann jemand eine gute Implementierung vorschlagen? Ich habe einen Mini-Unit-Test geschrieben. Wenn Sie in meinen Tests keinen Fehler sehen, sollte eine gute Implementierung die folgenden Kriterien erfüllen:

assert nearlyequal(1, 1, 5) 
assert nearlyequal(1.0, 1.0, 5) 
assert nearlyequal(1.0, 1.0, 5) 
assert nearlyequal(-1e-9, 1e-9, 5) 
assert nearlyequal(1e9, 1e9 + 1 , 5) 
assert not nearlyequal( 1e4, 1e4 + 1, 5) 
assert nearlyequal( 0.0, 1e-15, 5 ) 
assert not nearlyequal( 0.0, 1e-4, 6 ) 

Zusätzliche Bemerkungen:

  1. Die Werte a und b können vom Typ int, float oder numpy.float64 sein. Die Werte a und b sind immer vom gleichen Typ. Es ist wichtig, dass die Konvertierung keinen zusätzlichen Fehler in die Funktion einbringt.
  2. Wenn Sie diese Zahl beibehalten, sind Funktionen, die in Zeichenfolgen konvertiert werden oder nicht mathematische Tricks verwenden, nicht ideal. Dieses Programm wird von einem Mathematiker geprüft, der nachweisen möchte, dass die Funktion das tut, was sie tun soll.
  3. Geschwindigkeit ... Ich muss viele Zahlen vergleichen, je schneller desto besser.
  4. Ich habe numpy, scipy und die Standardbibliothek. Alles andere wird für mich schwer zu bekommen sein, besonders für einen so kleinen Teil des Projekts.
24
Salim Fadhley

Es gibt eine Funktion assert_approx_equal in numpy.testing (Quelle hier) , die ein guter Ausgangspunkt sein kann.

def assert_approx_equal(actual,desired,significant=7,err_msg='',verbose=True):
    """
    Raise an assertion if two items are not equal up to significant digits.

    .. note:: It is recommended to use one of `assert_allclose`,
              `assert_array_almost_equal_nulp` or `assert_array_max_ulp`
              instead of this function for more consistent floating point
              comparisons.

    Given two numbers, check that they are approximately equal.
    Approximately equal is defined as the number of significant digits
    that agree.
18
dF.

Ab Python 3.5 erfolgt dies standardmäßig (mithilfe der Standardbibliothek) mit der Funktion math.isclose .

Es hat die folgende Unterschrift:

isclose(a, b, rel_tol=1e-9, abs_tol=0.0)

Ein Anwendungsbeispiel mit absoluter Fehlertoleranz:

from math import isclose
a = 1.0
b = 1.00000001
assert isclose(a, b, abs_tol=1e-8)

Wenn Sie es mit einer Genauigkeit vonnsignifikanten Ziffern wollen, ersetzen Sie einfach die letzte Zeile durch:

assert isclose(a, b, abs_tol=10**-n)
48
erb

Hier ist eine Einstellung.

def nearly_equal(a,b,sig_fig=5):
    return ( a==b or 
             int(a*10**sig_fig) == int(b*10**sig_fig)
           )
8
Triptych

Ich glaube, Ihre Frage ist nicht gut genug definiert, und die Unit-Tests, die Sie vorlegen, beweisen es:

Wenn mit "auf N Nachkommastellen runden" "N Nachkommastellen rechts vom Dezimalpunkt" gemeint ist, sollte der Test assert nearlyequal(1e9, 1e9 + 1 , 5) fehlschlagen, da die Genauigkeit von 1000000000 und 1000000001 auf 0,00001 gerundet wird .

Und wenn Sie mit "auf N Nachkommastellen runden" "Die N höchstwertigen Stellen, unabhängig vom Dezimalpunkt" meinen, sollte der Test assert nearlyequal(-1e-9, 1e-9, 5) fehlschlagen, da sich 0,000000001 und -0,000000001 bei dieser Betrachtung völlig unterscheiden.

Wenn Sie die erste Definition gemeint haben, dann ist die erste Antwort auf dieser Seite (von Triptychon) gut. Wenn Sie die zweite Definition gemeint haben, sagen Sie es bitte, ich verspreche, darüber nachzudenken :-)

4
Oren Shemesh

Es gibt bereits viele gute Antworten, aber hier ist eine Überlegung:

def closeness(a, b):
  """Returns measure of equality (for two floats), in unit
     of decimal significant figures."""
  if a == b:
    return float("infinity")
  difference = abs(a - b)
  avg = (a + b)/2
  return math.log10( avg / difference )


if closeness(1000, 1000.1) > 3:
  print "Joy!"

Bei "signifikanten Zahlen" im Dezimalformat handelt es sich um das Anpassen des Dezimalpunkts und das Abschneiden auf eine Ganzzahl.

>>> int(3.1415926 * 10**3)
3141
>>> int(1234567 * 10**-3)
1234
>>>
2
S.Lott

Oren Shemesh hat einen Teil des Problems wie angegeben gelöst, aber es gibt noch mehr:

behaupten fast gleich (0.0, 1e-15, 5)

scheitert auch an der zweiten Definition (und das ist die Definition, die ich in der Schule gelernt habe.)

Egal wie viele Ziffern Sie betrachten, 0 entspricht nicht einer Nicht-Null. Dies kann zu Kopfschmerzen bei solchen Tests führen, wenn Sie einen Fall haben, dessen richtige Antwort Null ist.

1
Loren Pechtel

Hierfür gibt es eine interessante Lösung von B. Dawson (mit C++ - Code) unter "Compareing Floating Point Numbers" . Sein Ansatz beruht auf der strikten IEEE-Darstellung von zwei Zahlen und der erzwungenen lexikografischen Reihenfolge, wenn diese Zahlen als vorzeichenlose ganze Zahlen dargestellt werden.

1
Dan M.

Dies ist ein recht häufiges Problem bei Gleitkommazahlen. Ich löse es basierend auf der Diskussion in Abschnitt 1.5 von Demmel [1]. (1) Berechnen Sie den Rundungsfehler. (2) Vergewissern Sie sich, dass der Rundungsfehler kleiner als ein Epsilon ist. Ich habe Python seit einiger Zeit nicht mehr verwendet und habe nur Version 2.4.3, aber ich werde versuchen, dies richtig zu machen.

Schritt 1. Rundungsfehler

def roundoff_error(exact, approximate):
    return abs(approximate/exact - 1.0)

Schritt 2. Gleitkomma-Gleichheit

def float_equal(float1, float2, epsilon=2.0e-9):
    return (roundoff_error(float1, float2) < epsilon)

Dieser Code weist einige offensichtliche Mängel auf.

  1. Division durch Null Fehler, wenn der genaue Wert Null ist.
  2. Überprüft nicht, ob die Argumente Gleitkommawerte sind.

Revision 1.

def roundoff_error(exact, approximate):
    if (exact == 0.0 or approximate == 0.0):
        return abs(exact + approximate)
    else:
        return abs(approximate/exact - 1.0)

def float_equal(float1, float2, epsilon=2.0e-9):
    if not isinstance(float1,float):
        raise TypeError,"First argument is not a float."
    Elif not isinstance(float2,float):
        raise TypeError,"Second argument is not a float."
    else:
        return (roundoff_error(float1, float2) < epsilon)

Das ist ein bisschen besser. Wenn entweder der exakte oder der ungefähre Wert Null ist, ist der Fehler gleich dem Wert des anderen. Wenn etwas anderes als ein Gleitkommawert angegeben wird, wird ein TypeError ausgelöst.

Zu diesem Zeitpunkt ist es nur schwierig, den richtigen Wert für epsilon festzulegen. Ich habe in der Dokumentation zu Version 2.6.1 festgestellt, dass es in sys.float_info ein epsilon-Attribut gibt, sodass ich den doppelten Wert als Standard-epsilon verwenden würde. Der richtige Wert hängt jedoch sowohl von Ihrer Anwendung als auch von Ihrem Algorithmus ab.

[1] James W. Demmel, Angewandte Numerische Lineare Algebra , SIAM, 1997.

1
tmh

Ich wurde gebeten, eine von einem Drittanbieter bereitgestellte Bibliothek zu testen

Wenn Sie das Standard-Python-Framework unittest verwenden, können Sie assertAlmostEqual verwenden.

self.assertAlmostEqual(a, b, places=5)
0
serv-inc

Es gibt viele Möglichkeiten, zwei Zahlen zu vergleichen, um festzustellen, ob sie mit N signifikanten Ziffern übereinstimmen. Grob gesagt möchten Sie nur sicherstellen, dass ihre Differenz weniger als das 10 ^ -N-fache der größten der beiden verglichenen Zahlen beträgt. Das ist ganz einfach.

Aber was ist, wenn eine der Zahlen Null ist? Das gesamte Konzept der relativen Differenzen oder signifikanten Ziffern sinkt beim Vergleich mit Null. Um diesen Fall zu behandeln, müssen Sie auch eine absolute Differenz haben, die anders als die relative Differenz angegeben werden sollte.

Ich diskutiere die Probleme des Vergleichs von Gleitkommazahlen - einschließlich eines speziellen Falles der Behandlung von Null - in diesem Blog-Beitrag:

http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/

0
Bruce Dawson