Home > Blockchain >  I'm looking to make a progam which returns trigonometric values for any input
I'm looking to make a progam which returns trigonometric values for any input

Time:12-31

Using latest version of Python I'm using math module to calculate the trigonometric values but I'm not getting the Output I want. Program:

ans = math.sin(rad)
fracshun = ans.as_integer_ratio()
fracshun = str(fracshun[0]) "/" str(fracshun[1])
print("sin", degree, "°", " = ", fracshun, " = ", ans, sep="")

when I calculate sin of degree 45, the desired output is 1/√2, but this isn't the output I am getting, instead I get this

sin45.0° = 6369051672525773/9007199254740992 = 0.7071067811865476

And this is the case for other values like sin of 60 degree or cos of 30 degree and 45 degree How to print number as fractions that includes square roots(How to get "1/√2" from "0.7071067811865476")?

CodePudding user response:

Instead of using trigonometric functions from standard module math, you can use trigonometric functions from sympy.

sympy is a python module designed specifically for symbolic calculations. Calculations made with sympy won't evaluate into floating-point approximations unless you explicitly ask for it.

>>> import sympy as sp

>>> sp.sin(sp.pi/2)
1

>>> sp.sin(sp.pi/4)
sqrt(2)/2

>>> sp.sin(sp.pi/4).evalf()
0.707106781186548

CodePudding user response:

First, I'd like to note that we typically dislike having radicals in the denominator; and 1 / √2 = √2 / 2, so we typically prefer the latter.

Second, to express a floating-point number x as a multiple of √2, all you have to do is divide x by sqrt(2):

from math import sqrt

x = 0.7071067811865476
r = x / sqrt(2)
print('x = {} √2'.format(r))
# x = 0.5 √2

Now, you can see that the output is still showed with a decimal point, not with a fraction. Getting a fraction back from these floating-point numbers involves quite a bit of guessing, because floating-point numbers are approximations. For instance, you can "guess" that 0.5 is 1/2, and that 0.333 is 1/3. But 0.333 is not exactly equal to 1/3. How do we choose 1/3 rather than 3/10 or 33/100 or 333/1000?

Finding the "best fraction" corresponding to an approximate number is a topic that has been extensively studied. See this wikipedia article on the topic: Best rational approximations.

In python, the algorithms described in this wikipedia article are implemented in the standard library, in method Fraction.limit_denominator of class Fraction in module fractions.

Here is a function I wrote which divide a number by sqrt(2) then uses Fraction.limit_denominator to find a best approximating fraction and display the result nicely:

from math import sqrt
from fractions import Fraction

def float_to_sqrt2frac(x, max_denominator=180):
    y = Fraction(x).limit_denominator(max_denominator)
    r = Fraction(x / sqrt(2)).limit_denominator(max_denominator)
    if abs(y - x) <= abs(r*sqrt(2) - x):  # approximate as rational
        prettyones = {1: '1', -1: '-1'}
        m = ''
        r = y
    else:                                 # approximate as √2 fraction
        prettyones = {1: '', -1: '-'}
        m = '√2'
    if r == 0:
        return '0'
    else:
        num = prettyones.get(r.numerator, str(r.numerator))
        denom = '/{}'.format(r.denominator) if r.denominator != 1 else ''
        return m.join((num, denom))

You can toy around with the optional parameter max_denominator but if all your floating-point numbers were obtained as results of math.cos, math.sin or math.tan operations on relatively-precise angles, it shouldn't matter too much; and if the angles are too imprecise, you can't expect this function to guess whether the result should be a multiple of √2 or not anyway.

Some tests:

import sympy

def test_sqrt2(max_denominator=40):
    testcases = ['0', '1', '0.7', '0.707', '0.707107', '0.47', '1.1', '1.06', '1.0606', '1.06066', '3*sqrt(2)/4', 'sqrt(2)/8', '-sqrt(2)/4', '12*sqrt(2)/19', '-3*sqrt(2)/7', 'sin(pi/6)', 'sin(-pi/6)', 'cos(pi/6)', 'cos(45 * pi / 180)', 'sin(0.79)', 'sin(0.785)', 'sin(0.7854)']
    print('max_denominator={}'.format(max_denominator))
    for s in testcases:
        value = float(sympy.parse_expr(s).evalf())
        z = float_to_sqrt2frac(value, max_denominator=max_denominator)
        print('{:13s} ----> {}'.format(s, z))

>>> test_sqrt2()
max_denominator=40
0             ----> 0
1             ----> 1
0.7           ----> 7/10
0.707         ----> √2/2
0.707107      ----> √2/2
0.47          ----> 8/17
1.1           ----> 11/10
1.06          ----> 35/33
1.0606        ----> 35/33
1.06066       ----> 3√2/4
3*sqrt(2)/4   ----> 3√2/4
sqrt(2)/8     ----> √2/8
-sqrt(2)/4    ----> -√2/4
12*sqrt(2)/19 ----> 12√2/19
-3*sqrt(2)/7  ----> -3√2/7
sin(pi/6)     ----> 1/2
sin(-pi/6)    ----> -1/2
cos(pi/6)     ----> 13/15
cos(45 * pi / 180) ----> √2/2
sin(0.79)     ----> 27/38
sin(0.785)    ----> √2/2
sin(0.7854)   ----> √2/2
  • Related