math.sin() wertebereich

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
aw2
User
Beiträge: 2
Registriert: Mittwoch 25. September 2013, 17:51

Hallo,

bitte nicht schlagen, habe gerade ein Brett vor dem Kopf. Kann mir jemand die Rückgabewerte von math.sin() erklären?
Hätte Werte zwischen -1 und 1 erwartet; wieso ergibt sin(math.pi) einen Wert größer als 1? (1.22460635382e-16)

Beim Googeln fand ich z.B. auf http://www.tutorialspoint.com/python/number_sin.htm zur Sinusfunktion folgendes:
sin(x)
Return Value
This method returns a numeric value between -1 and 1, which represents the sine of the parameter x.
Let us compile and run the above program, this will produce the following result:
sin(3) : 0.14112000806
sin(-3) : -0.14112000806
sin(0) : 0.0
sin(math.pi) : 1.22460635382e-16
sin(math.pi/2) : 1
Klarer Widerspruch!

Ich brauche zu math.sin(x) die zugehörige y-Koordinate zwischen -1 und +1.
Muss man bestimmt irgendwie umrechnen, aber wie?
Mit der Bitte um Nachsicht, mein Matheunterricht ist schon fast vier Jahrzehnte her . . .

Danke für Denkanstöße

Axel
Sirius3
User
Beiträge: 17749
Registriert: Sonntag 21. Oktober 2012, 17:20

@aw2: Jetzt schauen wir erst mal, ob Deine Zahl tatsächlich größer als 1 ist:

Code: Alles auswählen

>>> 1.22460635382e-16 < 1
True
Puh, Glück gehabt. Die Mathematik stimmt immer noch.
Erklärung: Das "e" zeigt einen Exponenten an. Also 1.22*10^(-16)=0.000000000000000122.
BlackJack

@aw2: Die Zahl *ist* kleiner als 1. Sogar deutlich kleiner. Schau mal ans Ende, da steht e-10, was eine übliche Schreibweise für „mal 10 hoch -10” ist.

Code: Alles auswählen

In [3]: '%1.50f' % 1.22460635382e-16
Out[3]: '0.00000000000000012246063538200000751773409958202288'

In [4]: 1.22460635382e-16 < 1
Out[4]: True
aw2
User
Beiträge: 2
Registriert: Mittwoch 25. September 2013, 17:51

Danke für die Bestätigung, ich hatte den Verdacht schon beim ins Bett gehen. Aber warum gibt math.sin(math.pi) nicht exakt wieder 0? Liegt das daran, daß das OS auf 64Bit Auflösung begrenzt ist und Pi unendlich ist?
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

@aw2: Genau so ist es. Die symbolische Bedeutung von Pi kann halt in diesem Fall nicht aufrecht erhalten werden. Mit anderen Worten: Hinter `math.pi` steckt lediglich eine ungenaue Fließkommazahl. Wenn du diese Zahl an die Sinus-Funktion übergibst, dann "weiß" sie nichts über deren Ursprung und spuckt entsprechend stumpf ein etwas ungenaues Ergebnis aus. Du könntest `round()` verwenden, um das Ergebnis von `math.sin()` auf eine bestimmte Stellenzahl zu runden. Dann hättest du Ergebnisse, die etwas besser deinen Erwartungen entsprechen.
BlackJack

@aw2: Ja, daran liegt das. Wobei die Anzahl der verwendeten Bits für einen `float`-Wert nur bedingt am Betriebssystem liegt, sondern mehr an der Hardware beziehungsweise dem C-Compiler der verwendet wurde. Die Python-Dokumentation sagt zu `float`, dass da letztendlich der Datentyp ``double`` vom C-Compiler hinter steht. Auf Intel-x86-Hardware (und kompatiblen) ist dass dann üblicherweise etwas was 64 Bit verwendet. Unabhängig vom Betriebssystem und ob das nun ein 16-, 32-, oder 64-Bit-System ist.

Statt `round()` zu verwenden könnte man auch alleine die Darstellung auf eine bestimmte Anzahl von Stellen beschränken. Bei `round()` müsste man je nachdem was man berechnet hat nämlich darauf achten ob das Ergebnis dadurch genauer, oder ungenauer wird.
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

aw2 hat geschrieben:Liegt das daran, daß das OS auf 64Bit Auflösung begrenzt ist und Pi unendlich ist?
Nein und Ja.

Nicht das Betriebssystem ist auf 64-Bit begrenzt (das dient nur zur Bestimmung des maximal addressierbaren Speichers pro Prozess), sondern Fliesskommazahlen haben eine begrenzte Genauigkeit in diesem Fall (Python) sind es mit IEEE-756 Doubles zwar auch 64-Bit, aber das ist Zufall.

What Every Computer Scientist Should Know About Floating Point Arithmetic
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Spätestens bei `math.sin(math.pi/4)` hat man auch beim Ansatz des Rundens wieder nackte Fließkommazahlen, wo symbolisch eigentlich eine Wurzel geschrieben werden könnte. Man muss sich halt daran gewöhnen, dass Rechnen in der Informatik etwas anderes ist als Rechnen in der Mathematik. Ich verstehe aber, dasss man für Fälle, wo wirklich 0, 1, 1/2 oder ähnliches erwartet wird, die Ergebnisse entsprechend genau sein sollen. Deshalb finde ich etwas Generisches wie `round(x, 15)` manchmal nicht verkehrt.
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Wenn es eine fette Abhängigkeit sein darf oder wenn SymPy bereits installiert ist, dann geht natürlich auch folgendes:

Code: Alles auswählen

>>> import sympy
>>> sympy.sin(sympy.pi)
0
>>> sympy.sin(sympy.pi/2)
1
>>> sympy.sin(sympy.pi/2) + 10
11
>>> sympy.sin(sympy.pi/4) + 10
2**(1/2)/2 + 10
>>> float(sympy.sin(sympy.pi/4) + 10) == math.sin(math.pi/4) + 10
True
>>> sympy.sin(sympy.pi) == int(math.sin(math.pi))
True
usw...
Zuletzt geändert von snafu am Donnerstag 26. September 2013, 07:47, insgesamt 1-mal geändert.
BlackJack

Nur so als Ergänzung zu den `sympy`-Beispielen: Die Ergebnisse sehen so „harmlos” aus, darum sollte man sich den Typ anschauen:

Code: Alles auswählen

In [22]: sympy.sin(sympy.pi)
Out[22]: 0

In [23]: type(sympy.sin(sympy.pi))
Out[23]: sympy.core.numbers.Zero

In [24]: type(sympy.sin(sympy.pi / 2) + 10)
Out[24]: sympy.core.numbers.Integer

In [25]: sympy.sin(sympy.pi / 4)
Out[25]: 2**(1/2)/2
An irgendeiner Stelle möchte man dann vielleicht wieder zu „normalen” `float`-Werten zurück und sollte die `sympy`-Typen wieder umwandeln. :-)

Edit: Und da war snafu mit dem Ändern des Beitrags schneller als ich. :-D
Antworten