Skip to content

Trig & Hyp Functions

vmathmachine edited this page Jun 9, 2021 · 7 revisions

If you were looking for the inverse trigonometric/hyperbolic functions, please click here.

The 6 main trigonometric and 6 main hyperbolic functions are all implemented in this library. They're all public, static functions and are defined in the Cpx class.

Cosine, Sine, and Tangent (cos, sin, and tan) are all defined through their hyperbolic equivalents: cosh, sinh, and tanh.

sec, csc, cot, sech, csch, and coth are all defined as the reciprocal of cos, sin, tan, cosh, sinh, and tanh respectively. That is exaclty how they are defined in the code, and there's nothing else to it.

Implementation:

Complex cosh(Complex z)

Returns the hyperbolic cosine. The code first tests for 3 special cases, before using the default formula. In the default case, we utilize the formula cosh(z)=(e^z+e^-z)/2 to calculate the cosh function. To save on costly exponentials, we rearrange the terms and rewrite it as cosh(z)=(e^z+1/(e^z))*0.5. Thus, we only have to compute one exponential (e^z) instead of two (e^z and e^-z).

As I mentioned, though, there are 3 special cases. If the input is strictly real, the Math.cosh of the real part is returned. If the input is strictly imaginary, the Math.cos of the imaginary part is returned. If the real part is greater than 709 or less than -709, our default formula would return an overflow, even though the cosh function isn't supposed to overflow until the real part reaches ~710.4758.

To avoid an overflow, we first drop the least significant term, which gives us 0.5*e^z if z.re>0 and 0.5*e^-z if z.re<0. In other words, we get 0.5*e^abs2(z). If we rewrite that as e^(abs2(z)-ln(2)), then the overflow is fixed.

Complex sinh(Complex z)

Returns the hyperbolic sine. The code first tests for 4 special cases, before using the default formula. In the default case, we utilize the formula sinh(z)=(e^z-e^-z)/2 to calculate the sinh function. To save on costly exponentials, we rearrange the terms and rewrite it as sinh(z)=(e^z-1/(e^z))*0.5. Thus, we only have to compute one exponential (e^z) instead of two (e^z and e^-z).

As I mentioned, though, there are 4 special cases. If the input is strictly real, the Math.sinh of the real part is returned. If the input is strictly imaginary, i times the Math.sin of the imaginary part is returned. If the real part is greater than 709 or less than -709, the default formula would return an overflow, even though the sinh function isn't supposed to overflow until the real part reaches ~710.4758.

To avoid an overflow, we first drop the least significant term, which gives us 0.5*e^z if z.re>0 and -0.5*e^-z if z.re<0. In other words, we get 0.5*csgn(z)*e^abs2(z). If we rewrite that as csgn(z)*e^(abs2(z)-ln(2)), then the overflow is fixed.

The last special case is a roundoff that occurs when the input is too small. If the default formula were used on 1E-100, the formula would (in theory) give us (e^1E-100 - e^-1E-100)/2 which is about equal to ((1+1E-100)-(1-1E-100))/2 = (2E-100)/2 = 1E-10, which is the right answer. However, that's just what would happen in theory. In practice, double floating points don't have enough bits to store 1+1E-100 or 1-1E-100, and instead store them both as 1. Thus, we'd get (1-1)/2 = 0. To avoid this, whenever the lazy absolute value is less than 1E-4, we use the first two terms of the Taylor's series expansion of sinh(z) at 0, z+z³/6, to compute the sinh.

Complex tanh(Complex z)

Returns the hyperbolic tangent. The code first tests for 4 special cases, before using the default formula. In the default case, we utilize the formula tanh(z)=(e^z-e^-z)/(e^z+e^-z) to calculate the tanh function. To save on costly exponentials, we multiply the numerator and denominator by e^z, yielding tanh(z)=(e^(2z)-1)/(e^(2z)+1). Thus, we only have to compute one exponential (e^(2z)) instead of two (e^z and e^-z).

As mentioned, there are 4 special cases. If the input is strictly real, the Math.tanh of the real part is returned. If the input is strictly imaginary, i times the Math.tan of the imaginary part is returned. If the real part is greater than 300 or less than -300, the end result is completely indistinguishable from 1 or -1. Thus, we return 1 if the real part is positive, -1 if the real part is negative; we return csgn(z.re).

The last special case is a roundoff error that occurs when the input is too small. If the default formula were used on 1E-100, we'd once again face a situation where, due to lack of precision, e^z-e^-z is computed as 1-1=0. Literally the exact same as it was with the sinh. Once again, to fix this, we use the first two terms of the Taylor's series expansion whenever the lazy absolute value is less than 1E-4. For tanh, that expansion gives us z-z³/3.

Complex cos(Complex z)

Returns the cosine (in radians). All the function does is copy the input, multiply by i, then return the cosh.

Complex sin(Complex z)

Returns the sine (in radians). All the function does is copy the input, multiply by i, compute the sinh, multiply by -i, and return the result.

Complex tan(Complex z)

Returns the tangent (in radians). All the function does is copy the input, multiply by i, compute the tanh, multiply by -i, and return the result.

Complex sec(Complex z)

Complex csc(Complex z)

Complex cot(Complex z)

Returns the secant, cosecant, and cotangent respectively (in radians). All it does is compute the cosine/sine/tangent, then return the reciprocal.

Complex sech(Complex z)

Complex csch(Complex z)

Complex coth(Complex z)

Returns the hyperbolic secant, hyperbolic cosecant, and hyperbolic cotangent respectively. All it does is compute the hyperbolic cosine/sine/tangent, then return the reciprocal.

Background:

Due to Euler's Identity, we now know that angles and trigonometry are heavily tied together with complex numbers and exponentials.

One version of Euler's Identity is e^(Θi) = cos(Θ)+sin(Θ)i

(Where Θ is in radians)

Since nothing about this formula specifically restricts theta to being a real number, this allows us to extend trigonometric functions to all complex inputs. The most obvious way to extend the cosine and sine is just to define them as the real and imaginary parts of e^(Θi). However, such a definition is problematic, as it guarantees that the output can only ever be real. Not only that, but it also removes all the fancy properties that make the cosine and sine so useful. For instance, the identity cos²+sin²=1 would only be valid for real inputs.

An alternative way to define the cosine and sine is by observing that, if you apply Euler's identity to negative Θ, you get e^(-Θi) = cos(-Θ)+sin(-Θ)i = cos(Θ)-sin(Θ)i.

This means, if you take (e^(Θi)+e^(-Θi))/2, the sines will cancel out and we get cos(Θ).

Alternatively, if you take (e^(Θi)-e^(-Θi))/(2i), the cosines will cancel out and we get sin(Θ).

Under this definition, all the nice fancy properties of cosines and sines are preserved.

cos²Θ+sin²Θ = (e^(2Θi)+2e^0+e^(-2Θi))/4 + (e^(2Θi)-2e^0+e^(-2Θi))/(-4) = (e^(2Θi)+2+e^(-2Θi))/4 - (e^(2Θi)-2+e^(-2Θi))/4 = (e^(2Θi)+2+e^(-2Θi) -e^(2Θi)+2-e^(-2Θi))/4 = 4/4 = 1.

cos(Θ)cos(φ)-sin(Θ)sin(φ) = (e^(Θi)+e^(-Θi))(e^(φi)+e^(-φi))/4 - (e^(Θi)-e^(-Θi))(e^(φi)-e^(-φi))/(-4) = (e^(Θi)+e^(-Θi))(e^(φi)+e^(-φi))/4 + (e^(Θi)-e^(-Θi))(e^(φi)-e^(-φi))/4 = (e^(Θi)(e^(φi)+e^(-φi)) + e^(-Θi)(e^(φi)+e^(-φi)))/4 + (e^(Θi)(e^(φi)-e^(-φi)) - e^(-Θi)(e^(φi)-e^(-φi)))/4 = (e^(Θi)(2e^(φi)) + e^(-Θi)(2e^(-φi)) )/4 = (e^((Θ+φ)i) + e^(-(Θ+φ)i))/2 = cos(Θ+φ)

cos(Θ)sin(φ)+sin(Θ)cos(φ) = (e^(Θi)+e^(-Θi))(e^(φi)-e^(-φi))/(4i) + (e^(Θi)-e^(-Θi))(e^(φi)+e^(-φi))/(4i) = (e^(Θi)(e^(φi)-e^(-φi)) + e^(-Θi)(e^(φi)-e^(-φi)))/(4i) + (e^(Θi)(e^(φi)+e^(-φi)) - e^(-Θi)(e^(φi)+e^(-φi)))/(4i) = (e^(Θi)(2e^(φi)) - e^(-Θi)(2e^(-φi)) )/(4i) = (e^((Θ+φ)i) - e^(-(Θ+φ)i))/(2i) = sin(Θ+φ)

And so on and so forth.

As always, we also define the tangent to be the sine over cosine, which can be rewritten as (e^(Θi)-e^(-Θi))/(i(e^(Θi)+e^(-Θi))). With reciprocals, we also get the secant, cosecant, and cotangent.

In 1760, mathematicians defined another set of functions: the hyperbolic functions. These are essentially just a generalization of the original trigonometric functions, but with imaginary arguments.

cosh(x) is defined as cos(x*i)

sinh(x) is defined as sin(x*i)/i

tanh(x) is defined as tan(x*i)/i

And so on and so forth. In other words, the hyperbolic function is defined as the original function, but with the input multiplied by i. Then, we'd either multiply our answer by i, divide our answer by i, or do nothing. Which one we did really just depended on which option gave us a real output given real inputs.

The reason they're called hyperbolic functions is that, just as the trigonometric functions can be used to parameterize the unit circle, the hyperbolic functions can be used to parameterize the unit hyperbola.

The coordinates (cos(t), sin(t)) will always lie on the unit circle.

The coordinates (cosh(t), sinh(t)) will always lie on the unit hyperbola.

Of course, circles are far more common than hyperbolas, so it might initially seem a bit superfluous to do so. However, it's not just the shape we're concerned about. Many applications of trig functions arise from the very relation x²+y²=(some constant), which plots out a circle. Similarly, many applications of the hyperbolic functions arise from the relation x²-y²=(some constant), which plots out a hyperbola. The first identity can be satisfied not only by sine and cosine, mind you, but also by sech and tanh. Meanwhile, the second identity can be satisfied by sec and tan, csc and cot, cosh and sinh, or coth and csch.

There are also plenty of non-trig functions which satisfy these, such as the very simple (x²-y²)/(x²+y²), 2xy/(x²+y²) (which is the formula most commonly used to find rational Pythagorean triples).

There's also a surprising application of the hyperbolic functions, the catenary arc. Whenever you see a telephone wire sag, the shape it draws out is known as a catenary arc. Which, we now know today, is the shape drawn out by the function y=cosh(x) (or, rather, some shifted shaled version of that graph).

To be frank, there are many, many, many, many things that could be said about hyperbolic and trigonometric functions. Instead of spending several days listing them all, I'll just move on to a question that needs to be addressed:

Why does this library define trig functions in terms of hyperbolic functions?

I mean, as I said, the hyperbolic functions were initially defined in terms of trig functions. So it might seem strange that this library did it the other way around. However, there is some strong logic here. Put simply: the hyperbolic functions have a simpler definition than the trig functions.

As I said, the trig functions can be defined as:

cos(x)=(e^(xi)+e^(-xi))/2

sin(x)=(e^(xi)-e^(-xi))/(2i)

tan(x)=(e^(xi)-e^(-xi))/(i(e^(xi)+e^(-xi)))

I also said the hyperbolic functions can be defined as

cosh(x)=cos(xi)

sinh(x)=sin(xi)/i

tanh(x)=tan(xi)/i

If we plug these formulas into one another, we get

cosh(x)=(e^x+e^-x)/2

sinh(x)=(e^x-e^-x)/2

tanh(x)=(e^x-e^-x)/(e^x+e^-x)

You might notice that these new definitions have nothing to do with i. They could honestly just be written as rational exponents, no complex numbers needed. If we were to define the hyperbolic functions in terms of the trig functions, we'd have some unnecessary steps of multiplying by i, then dividing back by i, all the while nothing would really be gained as a result. I suppose it would, in the grand scheme of things, still be pretty easy for the computer, considering the hardest part here is the exponents. However, it would also make things a bit less intuitive. The formulas would be clunkier. There'd be some extra mulI's and divI's in the mix. It's honestly just easier to define them all in terms of the hyperbolic functions and go from there.

Clone this wiki locally