0

I'm trying to write a program that takes an angle in degrees, and approximates the sin and cos value based on a number of given terms that the user chooses. In case you don't know how to find sin and cos. So, with that being said, here is my current code:

import math
def main():
    print()
    print("Program to approximate sin and cos.")
    print("You will be asked to enter an angle and \na number of terms.")
    print("Written by ME")
    print()

    sinx = 0
    cosx = 0

    x = int(input("Enter an angle (in degrees): "))
    terms = int(input("Enter the number of terms to use: "))
    print()

    for i in range(1, terms+1):
        sinx = sinx + getSin(i, x)
        cosx = cosx + getCos(i, x)

    print(cosx, sinx)

def getSin(i, x):
    if i == 1:
        return x
    else:
        num, denom = calcSinFact(i, x)
        sin = num/denom
        return sin

def getCos(i, x):
    if i == 1:
        return 1
    else:
        num, denom = calcCosFact(i, x)
        cos = num/denom
        return cos

def calcSinFact(i, x):
    if i % 2 == 1:
        sign = -1
    if i % 2 == 0:
        sign = +1
    denom = math.factorial(i*2-1)
    num = sign * (x**(i*2-1))
    return num, denom

def calcCosFact(i, x):
    if i % 2 == 1:
        sign = -1
    if i % 2 == 0:
        sign = +1
    denom = math.factorial(i*2)
    num = sign * (x**(i*2))
    return num, denom

It runs but if i use the example shown in the picture above, i get cos = -162527117141.85715 and sin = -881660636823.117. So clearly something is off. In the picture above the answers should be cos = 0.50000000433433 and sin = 0.866025445100. I'm assuming it's the way I'm adding together the values in the first loop but i could be wrong. Any help is appreciated!

TooTone
  • 7,129
  • 5
  • 34
  • 60
user3330472
  • 89
  • 2
  • 10

4 Answers4

2

There are several issues here as pointed out in Russell Borogove's comments.

Issue no 1 is that the formulas you are using

enter image description here

(see wikipedia) expect x to be in radians not degrees. Going once round a circle is 360 degrees or 2*pi, so you can convert from degrees to radians by multipling by pi/180, as shown below in python code to incorrectly and then correctly get the sin of 90 degrees.

>>> math.sin(90)
0.8939966636005579
>>> math.sin(90*math.pi/180)
1.0

Issue no 2 is the rest of the code. As pointed out in the comments, there are some bugs, and the best way to find them would be to use some strategic print statements. However, you could write your program with far fewer lines of code, and simpler programs tend to have fewer bugs and be easier to debug if they do have problems.

As this is an assignment, I won't do it for you, but a related example is the series for sinh(x).

enter image description here

(again from wikipedia)

You can produce the terms in "one shot", using a Python list comprehension. The list can be printed and summed to get the result, as in the program below

x = 90 * math.pi / 180 # 90 degrees
n = 5
terms = [x**(2*i+1)/math.factorial(2*i+1) for i in range(n)]
print terms
sinh = sum(terms)
print sinh, math.sinh(x)

The output of this program is

[1.5707963267948966, 0.6459640975062462, 0.07969262624616703, 0.004681754135318687, 0.00016044118478735975]
2.30129524587 2.30129890231

I produced the Python list comprehension code directly from the mathematical formula for the summation, which is conveniently given in "Sigma" notation on the left hand side. You can produce sin and cos in a similar way. The one missing ingredient you need is the signs at each point in the series. The mathematical formulas tell you you need (-1)n. The Python equivalent is (-1)**n, which can be slotted into the appropriate place in the list comprehension code.

Community
  • 1
  • 1
TooTone
  • 7,129
  • 5
  • 34
  • 60
0

First, a few notes. It's better to print \n at the end of the previous print or at the beginning of the following then empty print(). It's useful to use a debugging tool, use logging module or just use print and find the bug by comparing expected values with returned values.

Here is a code that is working for me:

import math

def main():
    print()
    print("Program to approximate sin and cos.")
    print("You will be asked to enter an angle and \na number of terms.")
    print("Written by ME")
    print()

    sinx = 0
    cosx = 0

    x = int(input("Enter an angle (in degrees): "))
    terms = int(input("Enter the number of terms to use: "))
    print()

    x = x / 180.0 * math.pi; # added

    for i in range(1, terms+1):
        sinx = sinx + getSin(i, x)
        cosx = cosx + getCos(i, x)

    print("Cos:{0}, Sinus:{1}".format(cosx,sinx)); # changed

def getSin(i, x):
    if i == 1:
        return x
    else:
        num, denom = calcSinFact(i, x)
        sin = float(num)/denom # changed
        return sin

def getCos(i, x):
    if i == 1:
        return 1
    else:
        num, denom = calcCosFact(i, x)
        cos = float(num)/denom # changed
        return cos

def calcSinFact(i, x):
    if i % 2 == 1:
        sign = +1 # changed
    if i % 2 == 0:
        sign = -1 # changed
    denom = math.factorial(i*2-1)
    num = sign * (x**(i*2-1))
    return num, denom

def calcCosFact(i, x):
    if i % 2 == 1:
        sign = +1 # changed
    if i % 2 == 0:
        sign = -1 # changed
    denom = math.factorial(i*2-2) # changed
    num = sign * (x**(i*2-2)) # changed
    return num, denom

And what I have changed? (I hope I won't forget anything)

  1. Your sign variables were wrong. Just the opposite. So I changed the signes in the conditions.
  2. It is perhaps not necessary but I added conversion from int to float when you divided num by denom.
  3. According to the definition of the approximation, the input x is in radians. So I added conversion from degrees to radians. x = x / 180.0 * math.pi;
  4. Your index in calcCosFact was wrong. It was always higher by 2. (i.e. 4 instead of 2, 8 instead of 6...)

I got this result: Enter an angle (in degrees): 180 Enter the number of terms to use: 5 Cos:-0.976022212624, Sinus:0.00692527070751

It should be correct now. I can also recommend WolphramAlpha when you need to quickly do some math.

Marek
  • 815
  • 8
  • 19
0

Here is an improved version:

from math import radians
import sys

# version compatibility shim
if sys.hexversion < 0x3000000:
    # Python 2.x
    inp = raw_input
    rng = xrange
else:
    # Python 3.x
    inp = input
    rng = range

def type_getter(type):
    def fn(prompt):
        while True:
            try:
                return type(inp(prompt))
            except ValueError:
                pass
    return fn
get_float = type_getter(float)
get_int   = type_getter(int)

def calc_sin(theta, terms):
    # term 0
    num    = theta
    denom  = 1
    approx = num / denom
    # following terms
    for n in rng(1, terms):
        num *= -theta * theta
        denom *= (2*n) * (2*n + 1)
        # running sum
        approx += num / denom
    return approx

def calc_cos(theta, terms):
    # term 0
    num    = 1.
    denom  = 1
    approx = num / denom
    # following terms
    for n in rng(1, terms):
        num *= -theta * theta
        denom *= (2*n - 1) * (2*n)
        # running sum
        approx += num / denom
    return approx

def main():
    print(
        "\nProgram to approximate sin and cos."
        "\nYou will be asked to enter an angle and"
        "\na number of terms."
    )

    theta = get_float("Enter an angle (in degrees): ")
    terms = get_int  ("Number of terms to use: ")

    print("sin({}) = {}".format(theta, calc_sin(radians(theta), terms)))
    print("cos({}) = {}".format(theta, calc_cos(radians(theta), terms)))

if __name__=="__main__":
    main()

Note that because the Maclaurin series is centered on x=0, values of theta closer to 0 will converge much faster: calc_sin(radians(-90), 5) is -1.00000354258 but calc_sin(radians(270), 5) is -0.444365928238 (about 157,000 times further from the correct value of -1.0).

Hugh Bothwell
  • 55,315
  • 8
  • 84
  • 99
0

One can completely avoid all power and factorial computations by using and combining the recursive definitions of factorial and integer powers. Further optimization is obtained by computing both cos and sin values at once, so that the powers are only computed once.

PI = 3.1415926535897932384;

RadInDeg=PI/180;

def getCosSin(x, n):
    mxx = -x*x; 
    term = 1;
    k = 2;
    cossum = 1;
    sinsum = 1;
    for i in range(n):
        term *= mxx
        term /= k; k+=1
        cossum += term
        term /= k; k+=1
        sinsum += term
    return cossum, x*sinsum

def main():
    print "\nProgram to approximate sin and cos."
    print "You will be asked to enter an angle and \na number of terms."


    x = int(input("Enter an angle (in degrees): "))
    terms = int(input("Enter the number of terms to use: "))
    print 

    x = x*RadInDeg;

    cosx, sinx = getCosSin(x,terms) 

    print cosx, sinx

if __name__=="__main__":
    main()
Lutz Lehmann
  • 25,219
  • 2
  • 22
  • 51