By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
435,144 Members | 872 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 435,144 IT Pros & Developers. It's quick & easy.

math module broken?

P: n/a
Hi all

I was helping my niece with her trigonometry homework last night. Her
calculator's batteries were flat, so I thought I would use Python's
math module to calculate sin, cos, and tan.

I tried the example in the text book first, to ensure that I was
getting the correct result, but it did not agree. Then my wife had the
idea of using the Microsoft calculator in scientific mode, and that
one did give the correct result.

Here are some examples -

sin(32) -
Python 0.55142668
Microsoft 0.52991926

cos(32) -
Python 0.83422336
Microsoft 0.84804809

tan(32) -
Python 0.66100604
Microsoft 0.62486935

Version is Python 2.3.3. I get the same results on Linux and on
Windows 2000. I also get the same results using the cmath module.

Can someone please explain these discrepancies?

Thanks

Frank Millman
Jul 18 '05 #1
Share this Question
Share on Google+
16 Replies


P: n/a
fr***@chagford.com (Frank Millman) writes:
sin(32) -
Python 0.55142668
Microsoft 0.52991926
Python's number is the sin of 32 radians which is the same as the sin
of 0.584 radians. Microsoft's is the sin of 32 degrees which is 0.558
radians, so the results are coincidentally fairly close to one
another. Math libraries usually take args in radians while
calculators usually let you select between radians and degrees.
cos(32) -
Python 0.83422336
Microsoft 0.84804809

tan(32) -
Python 0.66100604
Microsoft 0.62486935


Same thing.
Jul 18 '05 #2

P: n/a
Frank Millman wrote:
Hi all

I was helping my niece with her trigonometry homework last night. Her
calculator's batteries were flat, so I thought I would use Python's
math module to calculate sin, cos, and tan.

I tried the example in the text book first, to ensure that I was
getting the correct result, but it did not agree. Then my wife had the
idea of using the Microsoft calculator in scientific mode, and that
one did give the correct result.

Here are some examples -

sin(32) -
Python 0.55142668
Microsoft 0.52991926
Version is Python 2.3.3. I get the same results on Linux and on
Windows 2000. I also get the same results using the cmath module.

Can someone please explain these discrepancies?


Both are "correct" if you know what you have been asking for.
In standard mathematics the argument to a trigonometric function like
sin, cos, tan, ... is in radians (!)
This is even the case with most pocket calculators but there you can switch
modes.
In the example above, Microsoft - something special as ever - seems to expect
the argument in degrees (which is quite unusual)

Since an argument in radians normally is between 0 and 2*pi or between -pi and pi,
an argument of 32 is unusual for a radians argument though perfectly legal.

If your input is in degrees, define

def mysin(x)
return math.sin(x/pi*180) # 1 degree = 180/pi radians (don's use x/180*pi)

now
print mysin(32) gives 0.529919264233
--
Helmut Jarausch

Lehrstuhl fuer Numerische Mathematik
RWTH - Aachen University
D 52056 Aachen, Germany
Jul 18 '05 #3

P: n/a
Helmut Jarausch <ja******@igpm.rwth-aachen.de> wrote in message
Both are "correct" if you know what you have been asking for.
In standard mathematics the argument to a trigonometric function like
sin, cos, tan, ... is in radians (!)
There was some discussion about this recently over in
comp.lang.fortran, arising when someone queried sin/cos/tan etc for
very large angles (represented in floating point, eg: sin(1.0E18)).
Reduction (angle modulo 2pi) is considerably easier if you use a
*rational* unit for measuring angles, as the answer can remain exact
in terms of the particular number represented in floating point.
Without using an irrational number, exact representations of angles
can actually exist where you also have exact representations of sines
and cosines. With radians you can only represent zero exactly, and
below it seems not even that!

Can someone supply a real concrete example where there is a reason to
prefer radians when computing sin/cos/tan? (Not their derivatives!)

The latter of the three examples is most disappointing:

C:\>python
Python 2.3.3 (#51, Dec 18 2003, 20:22:39) [MSC v.1200 32 bit (Intel)]
on win32
Type "help", "copyright", "credits" or "license" for more information.
from math import sin,asin,pi
sin(pi/6) # == 0.5 0.49999999999999994 degrees(asin(0.5)) # == 30 30.000000000000004 sin(pi) # == 0

1.2246063538223773e-016

I understand why this fails - I just think the world is wrong.
Represent angles using some rational fraction of a circle and all this
crap goes away. Why convert to a unit which eliminates the possibility
of an exact representation of any result at all?
In the example above, Microsoft - something special as ever - seems to expect
the argument in degrees (which is quite unusual)


The microsoft calculator has a checkbox for switching between degrees
and radians and gets all of the analytical results above correct. We
live in a mad world....

Jon
Jul 18 '05 #4

P: n/a
On 23 Jul 2004, Jon Wright wrote:
There was some discussion about this recently over in
comp.lang.fortran, arising when someone queried sin/cos/tan etc for
very large angles (represented in floating point, eg: sin(1.0E18)).
Reduction (angle modulo 2pi) is considerably easier if you use a
*rational* unit for measuring angles, as the answer can remain exact
in terms of the particular number represented in floating point.
Reduction... from 1.0e18?! Of course that doesn't work with radians, it
doesn't even works with degrees! 64-bit floating point numbers are only
accurate to +-64.0 at that magnitude:
(1.0e18+45)-1.0e18 0.0 (1.0e18+90)-1.0e17 128.0
Without using an irrational number, exact representations of angles
can actually exist where you also have exact representations of sines
and cosines. With radians you can only represent zero exactly, and
below it seems not even that!
So don't store your angles as radians. Mangle them, reduce them, whatever
if degrees / grads, and then convert them to radians before passing them
to the trig functions.
Can someone supply a real concrete example where there is a reason to
prefer radians when computing sin/cos/tan? (Not their derivatives!)
Because that's how the processor does it. If degrees were used in the
library, every sin() would have to be prefixed at the assembly level with
a multiply.

Derivates (and other mathematical reasons) are also perfectly valid
example. Every math / physics formula involving trigonometry uses
radians. If sin() accepted degrees, every formula I expressed in Python
would be littered with radian -> degree conversions. Quite confusing for
a researcher picking up Python for the first time. Python's done a good
job at winning over academia (mostly due to Numeric and numarray);
switching to degrees is a step in the wrong direction.

I'll skip over the obvious argument of purity here (i.e. switching trig
functions to degrees is the equivalent of switching to 1-based array
indexing).
The latter of the three examples is most disappointing:

C:\>python
Python 2.3.3 (#51, Dec 18 2003, 20:22:39) [MSC v.1200 32 bit (Intel)]
on win32
Type "help", "copyright", "credits" or "license" for more information.
from math import sin,asin,pi
sin(pi/6) # == 0.5 0.49999999999999994 degrees(asin(0.5)) # == 30 30.000000000000004 sin(pi) # == 0
1.2246063538223773e-016

I understand why this fails - I just think the world is wrong.
Represent angles using some rational fraction of a circle and all this
crap goes away. Why convert to a unit which eliminates the possibility
of an exact representation of any result at all?

.2 0.20000000000000001

The error in your calculations is of the same order of magnitude as that
of the error introduced by the representation of numbers as floating point
(not surprisingly, since the magnitude of the error of the value of pi is
caused by that same feature), and is therefore, for all practical
purposes, irrelevant.

What's more, because any method of calculating sines (including that used
by the processor) requires that its arguments be in radians, there's no
way around this error, no matter how hard you try (unless, of course, you
implement an 80- or even 128-bit floating point arithmetic library, in
which case it won't be included in Python because it will be an order of
magnitude slower than using the math coprocessor).
The microsoft calculator has a checkbox for switching between degrees
and radians and gets all of the analytical results above correct. We
live in a mad world....


I'm certain that's because it limits (reasonably) the accuracy of its
output, just like Python can do:
print sin(pi/6) # == 0.5 0.5 print degrees(asin(0.5)) # == 30 30.0 print sin(pi) # == 0 1.2246063538223773e-016

Oops, that last one didn't work. How about:
'%.15f'%sin(pi)

'0.000000000000000'

Personally, I'd like to see an application where any accuracy greater than
that is needed... and anyways, just how often do you expect angles of 30,
45, 60, and 90 degrees to come up in regular calculations? The chance of
one of them occuring randomly is infinitesmally small (except for in
high-school geometry homework, of course), and when they do come up
deterministically in a formula, they are almost always optimized out of
the equation.

Jul 18 '05 #5

P: n/a
[Christopher T King]
...
Reduction... from 1.0e18?! Of course that doesn't work with radians, it
doesn't even works with degrees!


Actually, it can. If you view a floating-point number as being
exactly what it says it is, then of course there's an exact reduction
of that number modulo pi. Very high-quality math libraries compute
that too. I know because I wrote one <wink>. The popular fdlibm
(from Netlib) does "as-if infinite precision" trig argument reduction
too. The details can be excruciating. You (of course) need to know
pi to greater than machine precision, but not to as much greater as
you may think: across all possible 754 doubles, you can find the one
closest to being an exact multiple of pi, and you only need to use
enough extra precision to get the right answer (to machine precision)
in that worst case. At least 3 groups have discovered that
independently (I was one of them, discovered when writing a libm for
Kendall Square Research in the early 90's, in collaboration with Peter
Tang).

IIRC, Mary Payne at DEC was the first to implement "infinite
precision" trig argument reduction in a commercial math library, and
she wrote a very readable paper about it I'm unable to find now. She
wasn't able to put an a priori bound on the amount of precision
needed, so it was less efficient than later attempts.

All in all, and despite having pushed the state of the art there
myself, it's a silly thng to bother with <wink>.
Jul 18 '05 #6

P: n/a
On Fri, 23 Jul 2004, Tim Peters wrote:
If you view a floating-point number as being exactly what it says it is,


then I have a proof that 1==2 to show you... ;)

Jul 18 '05 #7

P: n/a
Helmut Jarausch <ja******@igpm.rwth-aachen.de> wrote in message news:<41**************@igpm.rwth-aachen.de>...
....
If your input is in degrees, define

def mysin(x)
return math.sin(x/pi*180) # 1 degree = 180/pi radians (don's use x/180*pi)

now
print mysin(32) gives 0.529919264233


x/180*pi _is_ correct. Your formula gives mysin(32) = -0.9408618465702292.

Btw, another way to write this is:

def mysin(x):
return math.sin(math.radians(x))
Jul 18 '05 #8

P: n/a
da*****@yahoo.com (Dan Bishop) wrote in message news:<ad**************************@posting.google. com>...
Helmut Jarausch <ja******@igpm.rwth-aachen.de> wrote in message news:<41**************@igpm.rwth-aachen.de>...
...
If your input is in degrees, define

def mysin(x)
return math.sin(x/pi*180) # 1 degree = 180/pi radians (don's use x/180*pi)

now
print mysin(32) gives 0.529919264233


x/180*pi _is_ correct. Your formula gives mysin(32) = -0.9408618465702292.

Btw, another way to write this is:

def mysin(x):
return math.sin(math.radians(x))


Even better:

def mysin(x):
return math.sin(math.radians(x % 360))
Jul 18 '05 #9

P: n/a
da*****@yahoo.com (Dan Bishop) wrote in message news:<ad**************************@posting.google. com>...
Helmut Jarausch <ja******@igpm.rwth-aachen.de> wrote in message news:<41**************@igpm.rwth-aachen.de>...
...
If your input is in degrees, define

def mysin(x)
return math.sin(x/pi*180) # 1 degree = 180/pi radians (don's use x/180*pi)

now
print mysin(32) gives 0.529919264233


x/180*pi _is_ correct. Your formula gives mysin(32) = -0.9408618465702292.

Btw, another way to write this is:

def mysin(x):
return math.sin(math.radians(x))


Even better:

def mysin(x):
return math.sin(math.radians(x % 360))
Jul 18 '05 #10

P: n/a
On 23 Jul 2004 23:00:18 -0700,
da*****@yahoo.com (Dan Bishop) wrote:
da*****@yahoo.com (Dan Bishop) wrote in message news:<ad**************************@posting.google. com>...
def mysin(x):
return math.sin(math.radians(x)) Even better: def mysin(x):
return math.sin(math.radians(x % 360))


Why?
for x in range( 20 ):

print '%.12f %.12f' % (math.sin( math.radians( x ) ), math.sin( math.radians( x % 360 ) ))

0.000000000000 0.000000000000
0.017452406437 0.017452406437
0.034899496703 0.034899496703
0.052335956243 0.052335956243
0.069756473744 0.069756473744
0.087155742748 0.087155742748
0.104528463268 0.104528463268
0.121869343405 0.121869343405
0.139173100960 0.139173100960
0.156434465040 0.156434465040
0.173648177667 0.173648177667
0.190808995377 0.190808995377
0.207911690818 0.207911690818
0.224951054344 0.224951054344
0.241921895600 0.241921895600
0.258819045103 0.258819045103
0.275637355817 0.275637355817
0.292371704723 0.292371704723
0.309016994375 0.309016994375
0.325568154457 0.325568154457

Regards,
Dan

--
Dan Sommers
<http://www.tombstonezero.net/dan/>
Never play leapfrog with a unicorn.
Jul 18 '05 #11

P: n/a
Frank Millman wrote:
Hi all

I was helping my niece with her trigonometry homework last night. Her
calculator's batteries were flat, so I thought I would use Python's
math module to calculate sin, cos, and tan.
Can someone please explain these discrepancies?


Thank you so much for the explanations, everyone. I appreciate your
patience.

I agree with Timothy's suggestion about adding a note to the
documentation. Obviously the math module is designed for those that
know what they are doing. However, you will always get the odd guy
like me that has some vague recollection of trigonometry from school
days, but does not really understand it, so it should help to avoid
similar confusion in the future.

Frank
Jul 18 '05 #12

P: n/a
Dan Sommers wrote:
On 23 Jul 2004 23:00:18 -0700,
da*****@yahoo.com (Dan Bishop) wrote:

def mysin(x):
return math.sin(math.radians(x % 360))


Why?
for x in range( 20 ):
print '%.12f %.12f' % (math.sin( math.radians( x ) ), math.sin( math.radians( x % 360 ) ))

0.000000000000 0.000000000000
0.017452406437 0.017452406437
0.034899496703 0.034899496703
0.052335956243 0.052335956243
0.069756473744 0.069756473744
0.087155742748 0.087155742748
0.104528463268 0.104528463268
0.121869343405 0.121869343405
0.139173100960 0.139173100960
0.156434465040 0.156434465040
0.173648177667 0.173648177667
0.190808995377 0.190808995377
0.207911690818 0.207911690818
0.224951054344 0.224951054344
0.241921895600 0.241921895600
0.258819045103 0.258819045103
0.275637355817 0.275637355817
0.292371704723 0.292371704723
0.309016994375 0.309016994375
0.325568154457 0.325568154457

for x in range(10000000, 10000020): print '%.12f %.12f' %

(math.sin( math.radians( x ) ), math.sin( math.radians( x % 360 ) ))

-0.984807753010 -0.984807753012
-0.981627183449 -0.981627183448
-0.978147600733 -0.978147600734
-0.974370064789 -0.974370064785
-0.970295726278 -0.970295726276
-0.965925826288 -0.965925826289
-0.961261695942 -0.961261695938
-0.956304755964 -0.956304755963
-0.951056516293 -0.951056516295
-0.945518575604 -0.945518575599
-0.939692620787 -0.939692620786
-0.933580426495 -0.933580426497
-0.927183854571 -0.927183854567
-0.920504853453 -0.920504853452
-0.913545457639 -0.913545457643
-0.906307787041 -0.906307787037
-0.898794046299 -0.898794046299
-0.891006524183 -0.891006524188
-0.882947592863 -0.882947592859
-0.874619707138 -0.874619707139

When the numbers get larger, floats start losing precision. If you
restrict the numbers to the range [0, 360[ before conversion to radians
(and hence to degrees), that problem doesn't exist.

--
"Codito ergo sum"
Roel Schroeven
Jul 18 '05 #13

P: n/a
Roel Schroeven wrote:
When the numbers get larger, floats start losing precision. If you
restrict the numbers to the range [0, 360[ before conversion to radians
(and hence to degrees), that problem doesn't exist.

(360**100 +0.0) % 360

184.0

Rule of thumb: floats are always inaccurate. % may only help when you are
dealing with large integers denoting an angle in degrees - an unlikely
scenario methinks.

Peter

Jul 18 '05 #14

P: n/a
Peter Otten wrote:
Roel Schroeven wrote:

When the numbers get larger, floats start losing precision. If you
restrict the numbers to the range [0, 360[ before conversion to radians
(and hence to degrees), that problem doesn't exist.


(360**100 +0.0) % 360


184.0

Rule of thumb: floats are always inaccurate. % may only help when you are
dealing with large integers denoting an angle in degrees - an unlikely
scenario methinks.


Ok, you're right, I only considered the case where the input is
specified in degrees.

Anyway, when dealing with angles it's in most cases very well possible
to keep the values small by doing % 360 or % (2*pi) in all relevant
operations on the angles.

--
"Codito ergo sum"
Roel Schroeven
Jul 18 '05 #15

P: n/a
Dan Sommers <me@privacy.net> wrote in message news:<m2************@unique.fully.qualified.domain .name.yeah.right>...
On 23 Jul 2004 23:00:18 -0700,
da*****@yahoo.com (Dan Bishop) wrote:
da*****@yahoo.com (Dan Bishop) wrote in message news:<ad**************************@posting.google. com>...

def mysin(x):
return math.sin(math.radians(x))
Even better:

def mysin(x):
return math.sin(math.radians(x % 360))


Why?
for x in range( 20 ): print '%.12f %.12f' % (math.sin( math.radians( x ) ), math.sin( math.radians( x % 360 ) ))
[2 identical columns]


It's not suprising that they're identical: When 0 <= x < 360, then x % 360 == x.

However,
x = 360000000000L # a billion revolutions
math.sin(math.radians(x)) -6.6394736764063769e-08 math.sin(math.radians(x % 360))

0.0
Jul 18 '05 #16

P: n/a
On 24 Jul 2004 12:44:30 -0700,
da*****@yahoo.com (Dan Bishop) wrote:
Dan Sommers <me@privacy.net> wrote in message news:<m2************@unique.fully.qualified.domain .name.yeah.right>...
On 23 Jul 2004 23:00:18 -0700,
da*****@yahoo.com (Dan Bishop) wrote:
> da*****@yahoo.com (Dan Bishop) wrote in message news:<ad**************************@posting.google. com>...
>> def mysin(x):
>> return math.sin(math.radians(x))

> Even better:

> def mysin(x):
> return math.sin(math.radians(x % 360))


Why?
>>> for x in range( 20 ):

print '%.12f %.12f' % (math.sin( math.radians( x ) ), math.sin( math.radians( x % 360 ) ))
[2 identical columns]

It's not suprising that they're identical: When 0 <= x < 360, then x % 360 == x.


Yes, obviously, no surprise there. Duh! (smacks self on forehead)

I do, hoever, agree with Peter Otten about very large integers denoting
angles in degreen being unlikely. Usually, the 'mod 2pi' ends up in the
calculation of the angle rather than in the call to math.<whatever>.

Regards,
Dan

--
Dan Sommers
<http://www.tombstonezero.net/dan/>
Never play leapfrog with a unicorn.
Jul 18 '05 #17

This discussion thread is closed

Replies have been disabled for this discussion.