2009-05-08 4 views
21

ich kürzlich entdeckt, dass x**.5 und math.sqrt(x) nicht immer das gleiche Ergebnis in Python produzieren:Was ist genauer, x **. 5 oder math.sqrt (x)?

Python 2.6.1 (r261:67517, Dec 4 2008, 16:51:00) [MSC v.1500 32 bit (Intel)] 
on win32 
>>> 8885558**.5 - math.sqrt(8885558) 
-4.5474735088646412e-13 

alle ganzen Zahlen unter 10 ** 7 Überprüfung, erzeugen die beiden Methoden unterschiedliche Ergebnisse für fast genau 0,1% der Proben, mit der Größe des Fehlers, der für größere Zahlen (langsam) zunimmt.

Die Frage ist also, welche Methode ist genauer?

+0

Können Sie erklären, was Sie mit "Fehlerquote" meinen? –

+0

Die zwei Methoden zur Berechnung einer Quadratwurzel führen nicht zu identischen Ergebnissen für 10,103 Zahlen unter 10 ** 7. (Oder etwa 0,1%) –

+2

Ich sehe, das ist, was ich vermutete, dass Sie vielleicht gemeint haben. Sie werden wahrscheinlich feststellen, dass, wenn es einen Unterschied zwischen den beiden Berechnungsmethoden gibt, der Unterschied in dem am wenigsten signifikanten ein oder möglicherweise zwei Bits der Gleitkommadarstellung liegt. Dies wird als normal angesehen und ist eine Folge der verschiedenen Algorithmen, die zur Berechnung der Ergebnisse verwendet werden. –

Antwort

26

ist weder ein genauer, sie beide von der tatsächlichen Antwort divergieren in gleichen Teilen:

>>> (8885558**0.5)**2 
8885557.9999999981 
>>> sqrt(8885558)**2 
8885558.0000000019 

>>> 2**1023.99999999999 
1.7976931348498497e+308 

>>> (sqrt(2**1023.99999999999))**2 
1.7976931348498495e+308 
>>> ((2**1023.99999999999)**0.5)**2 
1.7976931348498499e+308 

>>> ((2**1023.99999999999)**0.5)**2 - 2**1023.99999999999 
1.9958403095347198e+292 
>>> (sqrt(2**1023.99999999999))**2 - 2**1023.99999999999 
-1.9958403095347198e+292 

http://mail.python.org/pipermail/python-list/2003-November/238546.html

Das mathematische Modul umschließt die Plattform C Bibliothek mathematische Funktionen die gleichen Namen; math.pow() ist besonders nützlich, wenn Sie brauchen (oder wollen einfach nur) hohe Kompatibilität mit C-Erweiterungen Aufruf C der pow().

__builtin__.pow() ist die Implementierung von Python Infix ** Operator und beschäftigt sich mit komplexen Zahlen, unbegrenzt ganzzahlige Potenzen und modularen Potenzierung zu (die C pow() nicht irgendwelche von denen handhaben).

** ist vollständiger. math.sqrt ist wahrscheinlich nur die C-Implementierung von sqrt, die wahrscheinlich mit pow verwandt ist.

+2

+1 für Link zu autorisierender Quelle :) –

+7

'sqrt' ist eine der grundlegenden Operationen definiert durch IEEE 754. Es sollte direkt implementiert werden, nicht als Aufruf an 'pow', und als Grundoperation sollte es korrekte Rundung haben (Welche üblichen Implementierungen von 'pow' haben nicht, bei weitem nicht). –

+3

Berechnung der jeweiligen Quadrate von '(8885558 ** 0,5)' und 'sqrt (8885558)' ** im Fließkomma mit der gleichen Genauigkeit wie die Potenz/Quadratwurzel ** und erwarte diese Berechnung, um irgendetwas über das Ergebnis zu sagen genauer ist naiv. Die Schlussfolgerung, dass "beide von der tatsächlichen Antwort zu gleichen Teilen abweichen", ist lächerlich: ihre Quadrate, die in Gleitkommazahlen berechnet werden, unterscheiden sich von 8885558.0 um 1ULP. –

10

Sowohl die pow-Funktion und die math.sqrt() Funktion kann Ergebnisse berechnen, die genauer sind als das, was der Standard-Schwimmertyp speichern kann. Ich denke, die Fehler, die Sie sehen, sind ein Ergebnis der Einschränkungen der Fließkomma-Mathematik und nicht der Ungenauigkeiten der Funktionen. Auch, seit wann ist ein Unterschied von ~ 10^(- 13) ein Problem, wenn man die Quadratwurzel einer 7-stelligen Zahl nimmt? Selbst die genauesten Physikberechnungen erfordern selten, dass viele signifikante Ziffern ...

Ein weiterer Grund für die Verwendung von math.sqrt() ist, dass es einfacher zu lesen und zu verstehen ist, was im Allgemeinen ein guter Grund ist, Dinge auf eine bestimmte Art zu tun.

+0

Die Antwort auf diese Frage scheint Ihrem Kommentar zur Geschwindigkeit zu widersprechen: http: // stackoverflow.com/questions/327002/python-which-ist-schneller-x-5-oder-math-sqrtx –

+0

Ein Freund von mir hat das heute früher selbst verglichen und festgestellt, dass math.sqrt() schneller war. Dies geschah im Rahmen einer Projekt-Euler-Berechnung. Ich vertraute seinem Wort, ohne seine Methode zu überprüfen, so dass die Behauptung falsch sein könnte. Dennoch scheint es sehr unwahrscheinlich, dass math.sqrt() langsamer ist. Wenn das der Fall war, warum implementierten sie es nicht einfach in pow()? –

+0

"in Bezug auf pow()" ... Ich bin ein bisschen müde. :) –

4

Jedes Mal, wenn Sie die Wahl zwischen zwei Funktionen haben, die in eine Sprache eingebaut sind, ist die spezifischere Funktion fast immer gleich oder besser als die generische (denn wenn es schlimmer wäre, hätten die Programmierer nur implementiert es in Bezug auf die generische Funktion). Sqrt ist spezifischer als allgemeine Potenzierung, so dass Sie erwarten können, dass es eine bessere Wahl ist. Und es ist zumindest in Bezug auf die Geschwindigkeit. In puncto Genauigkeit haben Sie es nicht mit genügend Genauigkeit in Ihren Zahlen zu tun, um zu erkennen.

Hinweis: Um zu verdeutlichen, ist sqrt in Python 3.0 schneller. In älteren Versionen von Python ist es langsamer. Siehe J. F. Sebastians Messungen unter Which is faster in Python: x**.5 or math.sqrt(x)?.

+0

Kannst du diesen letzten Satz für mich klären? –

+0

Ihr Punkt über Geschwindigkeit ist falsch. ** 0,5 ist tatsächlich schneller als sqrt. – Unknown

+0

Nicht in Python 3.0. – Brian

1

Ich bekomme nicht das gleiche Verhalten. Vielleicht ist der Fehler plattformspezifisch? Auf amd64 ich dieses:

 
Python 2.5.2 (r252:60911, Mar 10 2008, 15:14:55) 
[GCC 3.3.5 (propolice)] on openbsd4 
Type "help", "copyright", "credits" or "license" for more information. 
>>> import math 
>>> math.sqrt(8885558) - (8885558**.5) 
0.0 
>>> (8885558**.5) - math.sqrt(8885558) 
0.0 
+0

Ich hatte Angst, dass das der Fall sein könnte - ich habe irgendwo gelesen, dass math.sqrt direkt an die C-Implementierung übergeben wird, aber ich kann den Verweis jetzt nicht finden. –

+0

Ich kann das Ergebnis bestätigen. –

+0

Ben: Ich kann es nicht dokumentiert finden, aber es ist zumindest für Python 2.5 wahr: http://tinyurl.com/omvudo – Ken

3

Dies hat eine Art plattformspezifische Sache sein, weil ich unterschiedliche Ergebnisse erhalten:

Python 2.5.1 (r251:54863, Jan 13 2009, 10:26:13) 
[GCC 4.0.1 (Apple Inc. build 5465)] on darwin 
>>> 8885558**.5 - math.sqrt(8885558) 
0.0 

Welche Version von Python verwenden Sie und welche OS?

Meine Vermutung ist, dass es etwas mit Werbung und Casting zu tun hat. Mit anderen Worten, da Sie 8885558 **. 5 tun, muss 8885558 zu einem Float hochgestuft werden. All dies wird je nach Betriebssystem, Prozessor und Version von Python unterschiedlich gehandhabt. Willkommen in der wunderbaren Welt der Gleitkommaarithmetik. :-)

+0

"Python 2.6.1 (r261: 67517, 4. Dezember 2008, 16:51:00) [MSC v.1500 32 bit (Intel)] auf win32 " –

+0

Interessant. Vielleicht ist es ein 32-Bit-vs-64-Bit-Problem irgendeiner Art? Ich und die andere Person mit unterschiedlichen Ergebnissen verwenden beide ein 64-Bit-Betriebssystem. Oder es könnte eine BSD-Sache sein. Ich benutze Mac OS X. –

+0

Ich bekomme 0.0 auf 32-Bit-Linux (Python 2.5.2). –

6

Verwenden decimal präzisere Quadratwurzeln zu finden:

>>> import decimal 
>>> decimal.getcontext().prec = 60 
>>> decimal.Decimal(8885558).sqrt() 
Decimal("2980.86531061032678789963529280900544861029083861907705317042") 
3

ich mit Ihnen auf Win XP Python 2.5.1, das gleiche Problem bekam, während ich auf 32-Bit-Gentoo Python 2.5.4 nicht. Es ist eine Frage der Implementierung der C-Bibliothek. Jetzt

, auf Win, gibt math.sqrt(8885558)**28885558.0000000019, während (8885558**.5)**2 gibt 8885557.9999999981, die auf den gleichen epsilon betragen scheinen.

Ich sage, dass man nicht wirklich sagen kann, welche die "bessere" Option ist.

1

Theoretisch sollte math.sqrt eine höhere Genauigkeit haben als math.pow. Siehe Newtons Methode zur Berechnung von Quadratwurzeln [0]. Die Begrenzung der Anzahl der Dezimalstellen des Python-Floats (oder des C-Double) wird jedoch wahrscheinlich die Differenz maskieren.

[0] http://en.wikipedia.org/wiki/Integer_square_root

0

führte ich den gleichen Test und bekam die gleichen Ergebnisse, 10103 Unterschiede aus 10000000. Dieser Python 2.7 in Windows verwendet wurde.

Der Unterschied ist einer der Rundung. Ich glaube, wenn die zwei Ergebnisse sich unterscheiden, ist es nur ein ULP, der der kleinstmögliche Unterschied für einen Schwimmer ist. Die wahre Antwort liegt zwischen den beiden, aber eine float hat nicht die Fähigkeit, es genau darzustellen, und es muss abgerundet werden.

Wie in einer anderen Antwort erwähnt, kann die decimal module verwendet werden, um eine bessere Genauigkeit als eine float zu erhalten. Ich benutzte dies, um eine bessere Vorstellung von dem wahren Fehler zu bekommen, und in allen Fällen war die sqrt näher als die **0.5. Obwohl nicht viel!

>>> s1 = sqrt(8885558) 
>>> s2 = 8885558**0.5 
>>> s3 = decimal.Decimal(8885558).sqrt() 
>>> s1, s2, s3 
(2980.865310610327, 2980.8653106103266, Decimal('2980.865310610326787899635293')) 
>>> s3 - decimal.Decimal(s1) 
Decimal('-2.268290468226740188598632812E-13') 
>>> s3 - decimal.Decimal(s2) 
Decimal('2.2791830406379010009765625E-13')