Trigonometric functions: Difference between revisions

Content added Content deleted
(Replaced "radians" and "degrees" procs with "degTorRad" and "radToDeg" which are now provided by the module "math". Changed output formatting. Added output section.)
(Added Quackery.)
Line 4,054: Line 4,054:
Arctangent: 0.7853981633974483 45.0
Arctangent: 0.7853981633974483 45.0
>>> </lang>
>>> </lang>

=={{header|Quackery}}==

<code>v**</code> is defined at [[Exponentiation operator#Quackery]].

'''Please note''', the code presented here is sufficient to the task, but is not a practical implementation for the reasons discussed below. The intent of this entry is to invite discussion on the subject of Padé Approximants, the method used here. To that end I have opened a section on the subject in the Discussion page of this task, and invite you to contribute to it if you have useful knowledge of Padé Approximants.

Full disclosure - I am not a mathematician, I am an amateur programmer who has recently heard of Padé Approximants and is desirous of learning more, as they look to be a useful tool, but not a panacea.

A search of Rosetta Code at the time of writing (14 July 2021) finds no references to Padé or Pade on the site. A more general search of the Internet turns up such phrases as "is the "best" approximation of a function by a rational function of given order" and "The unreasonable effectiveness of Pade approximation", which piqued my interest. Generally there are scholarly papers in the subject that whoosh right over my head, and very little at the "pop-maths" level, i.e. no videos by my go-to YouTube channels - numberphile/computerphile, 3blue1brown, mythologer.

In the absence of sources pitched at my level, this is the methodology I have developed to create this code.

''Step 1''. Use Wolfram Alpha to find Padé Approximants for a function. Here is the relevant documentation for Mathematica, which also applies to Wolfram Alpha. Link: [https://reference.wolfram.com/language/ref/PadeApproximant.html PadeApproximant].

Here are the inputs to Wolfram Alpha used in generating this Quackery code. [https://www.wolframalpha.com/input/?i=PadeApproximant%5BSin%5Bx%5D%2C+%7Bx%2C+0%2C+%7B6%2C6%7D%7D%5D sin], [https://www.wolframalpha.com/input/?i=PadeApproximant%5Bcos%5Bx%5D%2C+%7Bx%2C+0%2C+%7B7%2C7%7D%7D%5D cos], [https://www.wolframalpha.com/input/?i=PadeApproximant%5Barccos%5Bx%5D%2C+%7Bx%2C+0%2C+%7B6%2C6%7D%7D%5D tan], [https://www.wolframalpha.com/input/?i=PadeApproximant%5Barcsin%5Bx%5D%2C+%7Bx%2C+0%2C+%7B6%2C6%7D%7D%5D arcsin], [https://www.wolframalpha.com/input/?i=PadeApproximant%5Barccos%5Bx%5D%2C+%7Bx%2C+0%2C+%7B6%2C6%7D%7D%5D+%29 arccos], and [https://www.wolframalpha.com/input/?i=PadeApproximant%5Barctan%5Bx%5D%2C+%7Bx%2C+0%2C+%7B7%2C7%7D%7D%5D arctan].

Note that the exact result for <code>arccos</code> includes several instances of the irrational number π, which is not ideal given that the intent is to generate a rational approximation, so instead I used the identity arccos(x)=π/2-arcsin(x), which Wolfram Alpha lists amongst the "Alternate forms", reducing the number of uses of π to one.

''Step 2''. Use GeoGebra to see the range of arguments over which the Padé approximant is valid, and to identify the range in which it will return values correct to a given number of decimal places. In each of the following examples, function <code>f</code> is a Padé Approximant, function <code>g</code> is the function that <code>f</code> is approximating, and function <code>h</code> is the difference between <code>f</code> and <code>g</code>, multiplied by <code>10^n</code>, where <code>n</code> can be varied with a slider. Where the <code>h</code> line is very close to zero, the approximation will be good to <code>n</code> decimal places.

Your attention is drawn to the task output for <code>arccos</code>, which is only good to a couple of decimal places for the argument passed to it. This is explained by the corresponding graph in Geogebra, where we can see that the argument is outside the safe (i.e. <code>h</code> is close to zero) range for anything other than very small values of <code>n</code>.

Geogebra graphs for the functions defined in this task: [https://www.geogebra.org/m/nygvcs2s sin], [https://www.geogebra.org/m/q3myjbfd cos], [https://www.geogebra.org/m/fsdfzzfs tan], [https://www.geogebra.org/m/n6jctj7c arcsin], [https://www.geogebra.org/m/kkrhjksu arccos], [https://www.geogebra.org/m/ge4qpppf arctan].

''Step 3''. Iterate over steps 1 and 2 until you find appropriate Padé Approximants for the task at hand, or conclude that none exist. Assuming the former;

''Step 4''. Code in a suitable language (i.e. probably not Quackery - efficiency was not a design criterion for Quackery, the language is intended to be the simplest possible introduction to Concatenative/Stack based programming, and is consequently suitable for hobbyist and educational use only) with any obvious optimisations, and use symmetries and identities of the function to extend the range of arguments that can be passed to it. (Not done here - the code serves solely to demonstrate the one-to-one correspondence between a proof-of-concept coding and the formula returned by Wolfram Alpha.)

Note also that the approximation of π/2 is good to 40 decimal places. This is intentional overkill, so that I can be sure that it is not the cause of any inaccuracies. Reducing the size of the numerator and denomination to more sensible values would be part of the optimisation process.

<lang Quackery> [ $" bigrat.qky' loadfile ] now!

[ 2646693125139304345
1684937174853026414 ] is pi/2 ( --> n/d )

[ 2dup
2dup 3 v** 2363 18183 v* v-
2over 5 v** 12671 4363920 v* v+
2swap 1 1
2over 2 v** 445 12122 v* v+
2over 4 v** 601 872784 v* v+
2swap 6 v** 121 16662240 v* v+
v/ ] is sin ( n/d --> n/d )

[ 1 1
2over 2 v** 3665 7788 v* v-
2over 4 v** 711 25960 v* v+
2over 6 v** 2923 7850304 v* v-
2swap 1 1
2over 2 v** 229 7788 v* v+
2over 4 v** 1 2360 v* v+
2swap 6 v** 127 39251520 v* v+
v/ ] is cos ( n/d --> n/d )
[ 2dup
2dup 3 v** 5 39 v* v-
2over 5 v** 2 715 v* v+
2over 7 v** 1 135135 v* v-
2swap 1 1
2over 2 v** 6 13 v* v-
2over 4 v** 10 429 v* v+
2swap 6 v** 4 19305 v* v-
v/ ] is tan ( n/d --> n/d )
[ 2dup
2dup 3 v** 2318543 2278617 v* v-
2over 5 v** 12022609 60763120 v* v+
2swap 1 1
2over 2 v** 1798875 1519078 v* v-
2over 4 v** 3891575 12152624 v* v+
2swap 6 v** 4695545 510410208 v* v-
v/ ] is arcsin ( n/d --> n/d )

[ pi/2 2swap arcsin v- ] is arccos ( n/d --> n/d )

[ 2dup
2dup 3 v** 50 39 v* v+
2over 5 v** 283 715 v* v+
2over 7 v** 256 15015 v* v+
2swap 1 1
2over 2 v** 21 13 v* v+
2over 4 v** 105 143 v* v+
2swap 6 v** 35 429 v* v+
v/ ] is arctan ( n/d --> n/d )

[ pi/2 v* 90 1 v/ ] is deg->rad ( n/d --> n/d )

[ pi/2 v/ 90 1 v* ] is rad->deg ( n/d --> n/d )

say "With an argument of 0.5 radians"
cr cr
$ "0.5" $->v drop
sin
say "Sin approximation: " 20 point$ echo$ cr
say " Actual value: 0.47942553860420300027..."
cr cr
$ "0.5" $->v drop
cos
say "Cos approximation: " 20 point$ echo$ cr
say " Actual value: 0.87758256189037271611..."
cr cr
$ "0.5" $->v drop
tan
say "Tan approximation: " 20 point$ echo$ cr
say " Actual value: 0.54630248984379051325..."
cr cr cr
say "To radians, using approximated values from previous computations"
cr cr
$ "0.47942553860423933121" $->v drop
arcsin
say "Arcsin approximation: " 20 point$ echo$ cr
say " Actual value: 0.5"
cr cr
$ "0.87758256189037190908" $->v drop
arccos
say "Arccos approximation: " 20 point$ echo$ cr
say " Actual value: 0.5"
cr cr
$ "0.54630248984379037103" $->v drop
arctan
say "Arctan approximation: " 20 point$ echo$ cr
say " Actual value: 0.5"
cr cr cr
say "0.5 radians is approx 28.64788976 degrees" cr
cr
$ "28.64788976" $->v drop
deg->rad sin
say "Sin approximation: " 20 point$ echo$ cr
say " Actual value: 0.47942553865718102604..."
cr cr
$ "28.64788976" $->v drop
deg->rad cos
say "Cos approximation: " 20 point$ echo$ cr
say " Actual value: 0.87758256186143068872..."
cr cr
$ "28.64788976" $->v drop
deg->rad tan
say "Tan approximation: " 20 point$ echo$ cr
say " Actual value: 0.54630248992217530618..."
cr cr cr
say "To degrees, using approximated values from previous computations"
cr cr
$ "0.47942553865721735699" $->v drop
arcsin rad->deg
say "Arcsin approximation: " 20 point$ echo$ cr
say " Actual value: 28.64788976..."
cr cr
$ "0.87758256186142988169" $->v drop
arccos rad->deg
say "Arccos approximation: " 20 point$ echo$ cr
say " Actual value: 28.64788976..."
cr cr
$ "0.54630248992217516396" $->v drop
arctan rad->deg
say "Arctan approximation: " 20 point$ echo$ cr
say " Actual value: 28.64788976..."</lang>

{{out}}

<pre>With an argument of 0.5 radians

Sin approximation: 0.47942553860423933121
Actual value: 0.47942553860420300027...

Cos approximation: 0.87758256189037190908
Actual value: 0.87758256189037271611...

Tan approximation: 0.54630248984379037103
Actual value: 0.54630248984379051325...


To radians, using approximated values from previous computations

Arcsin approximation: 0.49999997409078633068
Actual value: 0.5

Arccos approximation: 0.50090902435100642663
Actual value: 0.5

Arctan approximation: 0.50000000390223900073
Actual value: 0.5


0.5 radians is approx 28.64788976 degrees

Sin approximation: 0.47942553865721735699
Actual value: 0.47942553865718102604...

Cos approximation: 0.87758256186142988169
Actual value: 0.87758256186143068872...

Tan approximation: 0.54630248992217516396
Actual value: 0.54630248992217530618...


To degrees, using approximated values from previous computations

Arcsin approximation: 28.64788827551140385372
Actual value: 28.64788976...

Arccos approximation: 28.69997301874556855873
Actual value: 28.64788976...

Arctan approximation: 28.64788998358182581534
Actual value: 28.64788976...
Stack empty.
</pre>


=={{header|R}}==
=={{header|R}}==