# MATH: Functions using plain AppleScript.

A long way has been passed, and AppleScript can already do the math fast enough. I will try to replenish the list of functions in this topic:

``````
set x to pi / 3

my cos(x) -- RESULT: 0.5
my sin(x) -- RESULT: 0.866025
my exp(x) -- RESULT: 2.849654
my ln(x) -- RESULT: 0.046118
my tan(x) -- RESULT: 1.732051
my ctan(x) -- RESULT: 0.57735
my atan(x) -- RESULT: 0.808449
my arccot(x) -- RESULT: 0.762348
my arcsin(-pi / 4) -- RESULT: -0.903339
my arccos(-pi / 4) -- RESULT: 2.474135
my logyX(x / 9, 1 / 3) -- Logarithm of pi/27 with base 1/3 -- RESULT: 1.958022

----------------------- COSINE function ----------------------------------------------------------------
on cos(z as real)
set theAccuracy to 1.0E-6 --the precision
set z to z mod (2 * pi)
set aCos to 1
set a to 1
set n to 0
repeat
set n to n + 2
set a to -a * z * z / (n - 1) / n -- new Tailor serie's member
set aCos to aCos + a
if a < 0 then
if -a < theAccuracy then exit repeat
else
if a < theAccuracy then exit repeat
end if
end repeat
return aCos div 1 + (round 1000000 * (aCos mod 1)) / 1.0E+6
end cos

------------------------- SINE function --------------------------------------------------------------
on sin(z as real)
set theAccuracy to 1.0E-6 -- the precision
set z to z mod (2 * pi)
set aSin to z
set a to 1
set n to 0
repeat
set n to n + 2
set a to -a * z * z / (n - 1) / n
set adding to a * z / (n + 1) -- new Tailor serie's member
set aSin to aSin + adding
if -adding < theAccuracy then exit repeat
else
if adding < theAccuracy then exit repeat
end if
end repeat
return aSin div 1 + (round 1000000 * (aSin mod 1)) / 1.0E+6
end sin

---------------------- EXPONENT function -----------------------------------------------------------------
on exp(z as real)
set theAccuracy to 1.0E-6 -- the precision
set aExp to 1
set a to 1
set n to 1
repeat
set n to n + 1
set a to a * z / (n - 1)
set aExp to aExp + a -- add new Tailor serie's member
if a < 0 then
if -a < theAccuracy then exit repeat
else
if a < theAccuracy then exit repeat
end if
end repeat
return aExp div 1 + (round 1000000 * (aExp mod 1)) / 1.0E+6
end exp

----------------------- NATURAL LOGARITHM function ----------------------------------------------------------------
on ln(z as real) -- limitation: z>0
if z ≤ 0 then return "The parameter z should be >0"
set x to (z - 1) / (z + 1) -- convert big number z to number -1<x<1
set theAccuracy to 1.0E-6 -- the precision
set anLn to 2 * x -- first Tailor serie's member
set a to x -- part of Tailor serie's member
set n to 1 -- counter of Tailor serie's member
repeat
set n to n + 1
set a to a * x * x -- update part of Tailor serie's member
set adding to 2 * a / (2 * n - 1) -- new Tailor serie's member
set anLn to anLn + adding -- update ln's value
if adding < 0 then --
if -adding < theAccuracy then exit repeat
else
if adding < theAccuracy then exit repeat
end if
end repeat
return anLn div 1 + (round 1000000 * (anLn mod 1)) / 1.0E+6
end ln

------------------------- TANGENT function ---------------------------------------------------
on tan(z as real)
set z to (z + pi / 2) mod pi - pi / 2
if z = pi / 2 or z = -pi / 2 then return "Infinite Number: ∞"
set theAccuracy to 1.0E-6 -- the precision
set aSin to z
set aCos to 1
set a to 1
set aPreviousTan to 0
set n to 0
repeat
set n to n + 2
set a to -a * z * z / (n - 1) / n
set aCos to aCos + a -- add new Tailor serie's member
set aSin to aSin + a * z / (n + 1) -- add new Tailor serie's member
set atan to aSin / aCos
if atan > aPreviousTan then
if atan - aPreviousTan < theAccuracy then exit repeat
else
if aPreviousTan - atan < theAccuracy then exit repeat
end if
set aPreviousTan to atan
end repeat
return atan div 1 + (round 1000000 * (atan mod 1)) / 1.0E+6
end tan

------------------------- COTANGENT function --------------------------------------------
on ctan(z as real)
set x to (z + pi / 2) mod pi - pi / 2
if x = -pi or x = pi or x = 0 then return "Infinite Number: ∞"
set theAccuracy to 1.0E-6 -- the precision
set aSin to x
set aCos to 1
set a to 1
set aPreviousCotan to 1
set n to 0
repeat
set n to n + 2
set a to -a * x * x / (n - 1) / n
set aCos to aCos + a -- add new Tailor serie's member
set aSin to aSin + a * x / (n + 1) -- add new Tailor serie's member
set aCotan to aCos / aSin
if aCotan > aPreviousCotan then
if aCotan - aPreviousCotan < theAccuracy then exit repeat
else
if aPreviousCotan - aCotan < theAccuracy then exit repeat
end if
set aPreviousCotan to aCotan
end repeat
return aCotan div 1 + (round 1000000 * (aCotan mod 1)) / 1.0E+6
end ctan

------------------------- ARCTANGENT function --------------------------------------------
on atan(z)
set x to z
set theAccuracy to 1.0E-6
set k to 0.57735026919 -- tan(pi/6)
set k0 to 0.26794919243 -- tan(pi/12)
set flag to false -- segmentation flag

if z < 0 then set x to -x
if x > 1 then set x to 1 / x -- limit argument to 0..1
if x > k0 then set {flag, x} to {true, (x - k) / (1 + k * x)} -- determine segmentation

-- Argument is now < tan(pi/12). Approximate the function
set arcTang to x
set dx to x
set n to 1
repeat
set n to n + 1
set dx to -dx * x * x
set adding to dx / (2 * n - 1)
set arcTang to arcTang + adding
if adding < theAccuracy / (n - 1) then exit repeat
else
if -adding < theAccuracy / (n - 1) then exit repeat
end if
end repeat

if flag then set arcTang to arcTang + pi / 6 -- restore offset if needed
if z > 1 or z < -1 then set arcTang to pi / 2 - arcTang -- restore complement if needed
if z < 0 then set arcTang to -arcTang -- restore sign if needed
return arcTang div 1 + (round 1000000 * (arcTang mod 1)) / 1.0E+6
end atan

------------------------- ARCCOTANGENT function --------------------------------------------
on arccot(z)
set z to 1 / z
set x to z
set theAccuracy to 1.0E-6
set k to 0.57735026919 -- tan(pi/6)
set k0 to 0.26794919243 -- tan(pi/12)
set flag to false -- segmentation flag

if z < 0 then set x to -x
if x > 1 then set x to 1 / x -- limit argument to 0..1
if x > k0 then set {flag, x} to {true, (x - k) / (1 + k * x)} -- determine segmentation

-- Argument is now < tan(pi/12). Approximate the function
set arccoTang to x
set dx to x
set n to 1
repeat
set n to n + 1
set dx to -dx * x * x
set adding to dx / (2 * n - 1)
set arccoTang to arccoTang + adding
if adding < theAccuracy / (n - 1) then exit repeat
else
if -adding < theAccuracy / (n - 1) then exit repeat
end if
end repeat

if flag then set arccoTang to arccoTang + pi / 6 -- restore offset if needed
if z > 1 or z < -1 then set arccoTang to pi / 2 - arccoTang -- restore complement if needed
if z < 0 then set arccoTang to -arccoTang -- restore sign if needed
return arccoTang div 1 + (round 1000000 * (arccoTang mod 1)) / 1.0E+6
end arccot

------------------------- ARCSINE function --------------------------------------------
on arcsin(z)
if z < -1 or z > 1 then return "Parametr z should be ≥ -1 and ≤ 1"
if z = -1 then return 1.570796 -- pi/2
set z to z / (1 + (1 - z * z) ^ 0.5)
set x to z
set theAccuracy to 1.0E-6
set k to 0.57735026919 -- tan(pi/6)
set k0 to 0.26794919243 -- tan(pi/12)
set flag to false -- segmentation flag

if z < 0 then set x to -x
if x > 1 then set x to 1 / x -- limit argument to 0..1
if x > k0 then set {flag, x} to {true, (x - k) / (1 + k * x)} -- determine segmentation

-- Argument is now < tan(pi/12). Approximate the function
set arcSine to x
set dx to x
set n to 1
repeat
set n to n + 1
set dx to -dx * x * x
set adding to dx / (2 * n - 1)
set arcSine to arcSine + adding
if adding < theAccuracy / (n - 1) then exit repeat
else
if -adding < theAccuracy / (n - 1) then exit repeat
end if
end repeat

if flag then set arcSine to arcSine + pi / 6 -- restore offset if needed
if z > 1 or z < -1 then set arcSine to pi / 2 - arcSine -- restore complement if needed
if z < 0 then set arcSine to -arcSine -- restore sign if needed
return 2 * arcSine div 1 + (round 1000000 * (2 * arcSine mod 1)) / 1.0E+6
end arcsin

------------------------- ARCCOSINE function --------------------------------------------
on arccos(z)
if z < -1 or z > 1 then return "Parametr z should be ≥ -1 and ≤ 1"
if z = -1 then return -1.570796 -- -pi/2
set z to (1 - z * z) ^ 0.5 / (1 + z)
set x to z
set theAccuracy to 1.0E-6
set k to 0.57735026919 -- tan(pi/6)
set k0 to 0.26794919243 -- tan(pi/12)
set flag to false -- segmentation flag

if z < 0 then set x to -x
if x > 1 then set x to 1 / x -- limit argument to 0..1
if x > k0 then set {flag, x} to {true, (x - k) / (1 + k * x)} -- determine segmentation

-- Argument is now < tan(pi/12). Approximate the function
set arcCosine to x
set dx to x
set n to 1
repeat
set n to n + 1
set dx to -dx * x * x
set adding to dx / (2 * n - 1)
set arcCosine to arcCosine + adding
if adding < theAccuracy / (n - 1) then exit repeat
else
if -adding < theAccuracy / (n - 1) then exit repeat
end if
end repeat

if flag then set arcCosine to arcCosine + pi / 6 -- restore offset if needed
if z > 1 or z < -1 then set arcCosine to pi / 2 - arcCosine -- restore complement if needed
if z < 0 then set arcCosine to -arcCosine -- restore sign if needed
return 2 * arcCosine div 1 + (round 1000000 * (2 * arcCosine mod 1)) / 1.0E+6
end arccos

-----------------------  LOGARITHM of X with base Y  ------------------------------------------------
on logyX(zx as real, zy as real) -- limitation: zx>0 and zy≠1
if zx ≤ 0 then return "The parameter zx should be >0"
if zy ≤ 0 or zy = 1 then return "The base zy should be  >0 and ≠1"
set x to (zx - 1) / (zx + 1) --convert big number zx to number -1<x<1
set y to (zy - 1) / (zy + 1) --convert big number zy to number -1<y<1
set theAccuracy to 1.0E-6 -- the precision
set {Ln1, Ln2} to {2 * x, 2 * y} -- first Tailor serie's member
set anLn to x / y
set oldLn to anLn
set {a1, a2} to {x, y} -- parts of Tailor serie's member
set n to 1 -- counter of Tailor serie's member
repeat
set n to n + 1
set {a1, a2} to {a1 * x * x, a2 * y * y} -- update part of Tailor serie's member
set {adding1, adding2} to {2 * a1 / (2 * n - 1), 2 * a2 / (2 * n - 1)} -- new Tailor serie's members
set anLn to Ln1 / Ln2 -- update ln's value
if anLn > oldLn then --
if anLn - oldLn < 0.1 * theAccuracy then exit repeat
else
if oldLn - anLn < 0.1 * theAccuracy then exit repeat
end if
set oldLn to anLn
end repeat
return anLn div 1 + (round 1000000 * (anLn mod 1)) / 1.0E+6
end logyX

``````

UPDATE: added function Logarithm X based Y

Been a while since I logged in, so bumping this topic back up because I do love a good series expansion. The problem with a Maclaurin Series expansion when dealing with transcendental functions (such as sin, cos, e, ln, …) is that they require the summing of floating point numbers that end up being subject to huge rounding errors that accumulate with each iteration. This is particularly a problem here, because the handler above seeks a specific “accuracy” independent of the value of x. A Maclaurin series truncated after the term x^(n-1), mathematically, has an absolute error bounded by x^n/n!. Thus, with a small, fixed desired accuracy, as x gets larger, the value of n is going to have to be very large as well, which means: the number of iterations required becomes large; and the floating point precision of the machine or the software or whatever quickly becomes a limiting factor and errors start to accumulate. These errors become unbounded as x grows.

Here’s a table showing values of x=kπ/5 (first column, 0≤k≤100), the output of [u]sin/u as defined above, the true value of [u]sin/u accurate to within 10⁻¹⁵, and the absolute difference between the two values:
[format]0.0000000000 0.000000000000 0 0.0000000000
0.6283185307 0.587785252278 .587785252277944 -0.0000000000
1.2566370614 0.951056516284 .951056516284054 0.0000000000
1.8849555922 0.951056516281 .951056516280900 -0.0000000000
2.5132741229 0.58778525227 .587785252269686 -0.0000000000
3.1415926536 0.000000000000 -.000000000010206 -0.0000000000
3.7699111843 0.000000000000 -.587785252286201 -0.5877852523
4.3982297150 0.000000000000 -.951056516287208 -0.9510565163
5.0265482457 0.000000000000 -.951056516308648 -0.9510565163
5.6548667765 0.000000000000 -.587785252261429 -0.5877852523
6.2831853072 2.03893615832738E-11 .000000000020413 0.0000000000
6.9115038379 0.587785252294 .587785252294459 0.0000000000
7.5398223686 0.95105651629 .951056516290362 0.0000000000
8.1681408993 0.951056516305 .951056516305494 0.0000000000
8.7964594301 0.587785252253 .587785252253171 0.0000000000
9.4247779608 0.000000000000 -.000000000030620 -0.0000000000
10.0530964915 0.000000000000 -.587785252302716 -0.5877852523
10.6814150222 0.000000000000 -.951056516293516 -0.9510565163
11.3097335529 0.000000000000 -.951056516302339 -0.9510565163
11.9380520836 0.000000000000 -.587785252325816 -0.5877852523
12.5663706144 3.83288979950519E-11 .000000000040827 0.0000000000
13.1946891451 0.587785252292 .587785252310974 0.0000000000
13.8230076758 0.951056516284 .951056516296670 0.0000000000
14.4513262065 0.951056516316 .951056516299185 -0.0000000000
15.0796447372 0.587785252293 .587785252317558 0.0000000000
15.7079632679 3.54833389629793E-11 .000000000048966 0.0000000000
16.3362817987 0.000000000000 -.587785252319231 -0.5877852523
16.9646003294 0.000000000000 -.951056516299824 -0.9510565163
17.5929188601 0.000000000000 -.951056516296031 -0.9510565163
18.2212373908 0.000000000000 -.587785252309301 -0.5877852523
18.8495559215 3.47280164084486E-10 -.000000000038759 -0.0000000004
19.4778744523 0.587785243896 .587785252327488 0.0000000084
20.1061929830 0.95105650903 .951056516302978 0.0000000073
20.7345115137 0.951056521218 .951056516292877 -0.0000000049
21.3628300444 0.587785233174 .587785252301043 0.0000000191
21.9911485751 7.02924667711419E-8 .000000000028552 -0.0000000703
22.6194671058 0.000000000000 -.587785252254844 -0.5877852523
23.2477856366 0.000000000000 -.951056516306132 -0.9510565163
23.8761041673 0.000000000000 -.951056516289723 -0.9510565163
24.5044226980 0.000000000000 -.587785252292786 -0.5877852523
25.1327412287 0.000000000000 -.000000000018345 -0.0000000000
25.7610597594 0.587783153173 .587785252263102 0.0000020991
26.3893782902 0.951061327886 .951056516309287 -0.0000048116
27.0176968209 0.951056347512 .951056516286569 0.0000001688
27.6460153516 0.587772317186 .587785252284528 0.0000129351
28.2743338823 8.54012014885689E-6 .000000000008139 -0.0000085401
28.9026524130 0.000000000000 -.587785252271359 -0.5877852523
29.5309709437 0.000000000000 -.951056516281539 -0.9510565163
30.1592894745 0.000000000000 -.951056516283415 -0.9510565163
30.7876080052 0.000000000000 -.587785252276271 -0.5877852523
31.4159265359 0.000000000000 .000000000002067 0.0000000000
32.0442450666 0.587805814225 .587785252279617 -0.0000205619
32.6725635973 0.950347590122 .951056516284693 0.0007089262
33.3008821281 0.950799336863 .951056516280261 0.0002571794
33.9292006588 0.595269847554 .587785252268014 -0.0074845953
34.5575191895 0.005938793299 -.000000000012274 -0.0059387933
35.1858377202 0.000000000000 -.587785252287874 -0.5877852523
35.8141562509 0.000000000000 -.951056516287847 -0.9510565163
36.4424747816 0.000000000000 -.951056516308009 -0.9510565163
37.0707933124 0.000000000000 -.587785252259756 -0.5877852523
37.6991118431 0.051791763587 .000000000022481 -0.0517917636
38.3274303738 0.106449490749 .587785252296131 0.4813357615
38.9557489045 0.841843561757 .951056516291001 0.1092129545
39.5840674352 3.818935840702 .951056516304855 -2.8678793244
40.2123859659 9.763664987574 .587785252332400 -9.1758797352
40.8407044967 82.636861597699 -.000000000032687 -82.6368615977
41.4690230274 372.346171564771 -.587785252304389 -372.9339568171
42.0973415581 1746.36832534517 -.951056516294155 -1747.3193818615
42.7256600888 7987.8413836054 -.951056516301701 -7988.7924401217
43.3539786195 3.5403419011537E+4 -.587785252324143 -35404.0067967893
43.9822971503 1.55108711152585E+5 .000000000042894 -155108.7111525850
44.6106156810 6.66006641759216E+5 .587785252312646 -666006.0539739636
45.2389342117 2.79857057954168E+6 .951056516297309 -2798569.6284851637
45.8672527424 1.15366903147472E+7 .951056516298546 -11536689.3636906836
46.4955712731 4.66381622020993E+7 .587785252315885 -46638161.6143140495
47.1238898038 1.85019522893343E+8 .000000000046898 -185019522.8933430016
47.7522083346 7.20628383191789E+8 -.587785252320904 -720628383.7795742750
48.3805268653 2.75709441350816E+9 -.951056516300463 -2757094414.4592165947
49.0088453960 1.03664360736406E+10 -.951056516295392 -10366436074.5916576385
49.6371639267 3.83210521120268E+10 -.587785252307628 -38321052112.6145858765
50.2654824574 1.39334761842715E+11 -.000000000036691 -139334761842.7149963379
50.8938009882 4.9851203633058E+11 .587785252329161 -498512036329.9922485352
51.5221195189 1.75572681922613E+12 .951056516303617 -1755726819225.1787109375
52.1504380496 6.08932863500673E+12 .951056516292238 -6089328635005.7792968750
52.7787565803 2.08052229787237E+13 .587785252299371 -20805222978723.1132812500
53.4070751110 7.00518804816674E+13 .000000000026485 -70051880481667.3984375000
54.0353936417 2.32520005961312E+14 -.587785252256517 -232520005961312.5937500000
54.6637121725 7.61092395645824E+14 -.951056516306771 -761092395645825.0000000000
55.2920307032 2.45747719370812E+15 -.951056516289084 -2457477193708121.0000000000
55.9203492339 7.82979665837412E+15 -.587785252291113 -7829796658374121.0000000000
56.5486677646 2.4623495850696E+16 -.000000000016278 -24623495850696000.0000000000
57.1769862953 7.64561191516067E+16 .587785252264774 -76456119151606704.0000000000
57.8053048261 2.34454774755079E+17 .951056516309925 -234454774755079008.0000000000
58.4336233568 7.10243501926409E+17 .951056516285930 -710243501926408960.0000000000
59.0619418875 2.12603361000006E+18 .587785252282856 -2126033610000059904.0000000000
59.6902604182 6.29009748565338E+18 .000000000006071 -6290097485653380096.0000000000
60.3185789489 1.83981986059481E+19 -.587785252273032 -18398198605948100608.0000000000
60.9468974796 5.3214120703196E+19 -.951056516282178 -53214120703195996160.0000000000
61.5752160104 1.5223410603455E+20 -.951056516282776 -152234106034550013952.0000000000
62.2035345411 4.30851195989386E+20 -.587785252274598 -430851195989386002432.0000000000[/format]

You’ll probably notice a pattern in the way the error is distributed, which isn’t entirely down to the float point precision of our machines. It’s inherent in the Taylor series expansions themselves, which expand around a given point (in the case above, around x=0, which gives us the Maclaurin Series), close to which the number of terms required for a high degree of accuracy is small (in fact, [u]sin/u≈x for very small x), but as one moves away from the centre of expansion, the error due to truncating a Taylor expansion at the nth term increases, steadily until it erupts, which it has a tendency to do as you get closer to any point at which the series expanded around the point x=a vanishes, i.e. [u]sin/u=0, with the exception of a itself. Hence you see those blocks of lines where the absolute error is 0.0000… in between intervals of π.

This all applies to the other transcendental functions as well. But, besides all of that, Taylor Series are also pretty slow to converge and, as I described, have a bad time around vanishing points of a function.

One method you can use to mitigate all of this is to use the Maclaurin Series—which is accurate for small values of x—and restrict the range within which it operates to those values of x that are close to 0. Since all of the trigonometric functions are cyclic, this is pretty straight forward, since [u]sin/u=(−1)^n∙[u]sin/u. Additionally, you’d use the fact that [u]sin/u=2[u]sin/u∙[u]cos/u, which allows you halve the value of any input passed to your function as much required to make it arbitrarily small (subject to the machine epsilon).

Probably one of the best (i.e. most efficient, fastest, and most accurate) ways is to approximate the trig functions using Chebyshev polynomials. But practically speaking, I think the optimal methods are ones that store pre-calculated values of a function for carefully chosen values of x, then use a minimum number of terms (literally one or two) from a series expansion (Maclaurin is fine) to obtain the in-between values.

PS. I wouldn’t use z as a variable name in this context, as that implies that the handlers accept complex numbers.

Thank you for your interest in this topic. Mine code with which you calculated here the sine is taken from another topic. There, Fredrik71 used it in Numbers.app to build a circle. Hardly in Numbers.app are useful argument values outside the range of -2pi- + 2pi. So, there the code is written fast and simply.

In this Code Exchange’s topic, the area of the argument deliberately decreases, as you advise correctly too. But still it was nice to meet another person who is well versed in mathematics.