* Copyright (c) 1994 David I. Bell
* Permission is granted to use, distribute, or modify this source,
* provided that this copyright notice remains intact.
* Transcendental functions for real numbers.
* These are sin, cos, exp, ln, power, cosh, sinh.
BOOL _sinisneg_
; /* whether sin(x) < 0 (set by cos(x)) */
* Calculate the cosine of a number with an accuracy within epsilon.
* This also saves the sign of the corresponding sin function.
NUMBER
*term
, *sum
, *qsq
, *epsilon2
, *tmp
;
if (qisneg(epsilon
) || qiszero(epsilon
))
math_error("Illegal epsilon value for cosine");
bits
= qprecision(epsilon
) + 1;
epsilon
= qscale(epsilon
, -4L);
* If the argument is larger than one, then divide it by a power of two
* so that it is one or less. This will make the series converge quickly.
* We will extrapolate the result for the original argument afterwards.
scale
= zhighbit(q
->num
) - zhighbit(q
->den
) + 1;
tmp
= qscale(epsilon
, -scale
);
epsilon2
= qscale(epsilon
, -4L);
bits2
= qprecision(epsilon2
) + 10;
* Now use the Taylor series expansion to calculate the cosine.
* Keep using approximations so that the fractions don't get too large.
while (qrel(term
, epsilon2
) > 0) {
term
= qdivi(tmp
, (long) i
);
tmp
= qbround(term
, bits2
);
sum
= qbround(tmp
, bits2
);
* Now scale back up to the original value of x by using the formula:
* cos(2 * x) = 2 * (cos(x) ^ 2) - 1.
_sinisneg_
= !_sinisneg_
;
sum
= qbround(tmp
, bits2
);
tmp
= qbround(sum
, bits
);
* Calculate the sine of a number with an accuracy within epsilon.
* This is calculated using the formula:
* sin(x)^2 + cos(x)^2 = 1.
* The only tricky bit is resolving the sign of the result.
* Future: Use sin(3*x) = 3*sin(x) - 4*sin(x)^3.
NUMBER
*tmp1
, *tmp2
, *epsilon2
;
if (qisneg(epsilon
) || qiszero(epsilon
))
math_error("Illegal epsilon value for sine");
epsilon2
= qsquare(epsilon
);
tmp1
= qcos(q
, epsilon2
);
tmp2
= qlegtoleg(tmp1
, epsilon
, _sinisneg_
);
* Calculate the tangent function.
NUMBER
*cosval
, *sinval
, *epsilon2
, *tmp
, *res
;
if (qisneg(epsilon
) || qiszero(epsilon
))
math_error("Illegal epsilon value for tangent");
epsilon2
= qsquare(epsilon
);
cosval
= qcos(q
, epsilon2
);
sinval
= qlegtoleg(cosval
, epsilon2
, _sinisneg_
);
tmp
= qdiv(sinval
, cosval
);
res
= qbround(tmp
, qprecision(epsilon
) + 1);
* Calculate the arcsine function.
* The result is in the range -pi/2 to pi/2.
NUMBER
*sum
, *term
, *epsilon2
, *qsq
, *tmp
;
if (qisneg(epsilon
) || qiszero(epsilon
))
math_error("Illegal epsilon value for arcsine");
if ((qrel(q
, &_qone_
) > 0) || (qrel(q
, &_qnegone_
) < 0))
math_error("Argument too large for asin");
epsilon
= qscale(epsilon
, -4L);
epsilon2
= qscale(epsilon
, -4L);
* If the argument is too near one (we use .5) then reduce the
* argument to a more accurate range using the formula:
* asin(x) = 2 * asin(sqrt((1 - sqrt(1 - x^2)) / 2)).
if (qrel(q
, &_qonehalf_
) > 0) {
sum
= qlegtoleg(q
, epsilon2
, FALSE
);
tmp
= qsub(&_qone_
, sum
);
tmp
= qsqrt(sum
, epsilon2
);
sum
= qasin(tmp
, epsilon
);
* Argument is between zero and .5, so use the series.
epsilon
= qscale(epsilon
, -4L);
epsilon2
= qscale(epsilon
, -4L);
bits
= qprecision(epsilon
) + 1;
while (qrel(term
, epsilon2
) > 0) {
term
= qmul(tmp
, &mulnum
);
tmp
= qbround(term
, bits2
);
sum
= qbround(tmp
, bits2
);
tmp
= qbround(sum
, bits
);
* Calculate the acos function.
* The result is in the range 0 to pi.
NUMBER
*tmp1
, *tmp2
, *tmp3
, *epsilon2
;
if (qisneg(epsilon
) || qiszero(epsilon
))
math_error("Illegal epsilon value for arccosine");
if ((qrel(q
, &_qone_
) > 0) || (qrel(q
, &_qnegone_
) < 0))
math_error("Argument too large for acos");
* Calculate the result using the formula:
* acos(x) = asin(sqrt(1 - x^2)).
* The formula is only good for positive x, so we must fix up the
* result for negative values.
epsilon2
= qscale(epsilon
, -8L);
tmp1
= qlegtoleg(q
, epsilon2
, FALSE
);
tmp2
= qasin(tmp1
, epsilon
);
* For negative values, we need to subtract the asin from pi.
tmp1
= qbround(tmp3
, qprecision(epsilon
) + 1);
* Calculate the arctangent function with a accuracy less than epsilon.
* atan(x) = asin(sqrt(x^2 / (x^2 + 1))).
NUMBER
*tmp1
, *tmp2
, *tmp3
, *epsilon2
;
if (qisneg(epsilon
) || qiszero(epsilon
))
math_error("Illegal epsilon value for arctangent");
epsilon2
= qscale(epsilon
, -8L);
tmp1
= qsqrt(tmp3
, epsilon2
);
tmp2
= qasin(tmp1
, epsilon
);
* Calculate the angle which is determined by the point (x,y).
* This is the same as arctan for non-negative x, but gives the correct
* value for negative x. By convention, y is the first argument.
* For example, qatan2(1, -1) = 3/4 * pi.
NUMBER
*qy
, *qx
, *epsilon
;
NUMBER
*tmp1
, *tmp2
, *epsilon2
;
if (qisneg(epsilon
) || qiszero(epsilon
))
math_error("Illegal epsilon value for atan2");
if (qiszero(qy
) && qiszero(qx
)) {
/* conform to 4.3BSD ANSI/IEEE 754-1985 math lib */
* If the point is on the negative real axis, then the answer is pi.
if (qiszero(qy
) && qisneg(qx
))
* If the point is in the right half plane, then use the normal atan.
if (!qisneg(qx
) && !qiszero(qx
)) {
tmp2
= qatan(tmp1
, epsilon
);
* The point is in the left half plane. Calculate the angle by finding
* the atan of half the angle using the formula:
* atan2(y,x) = 2 * atan((sqrt(x^2 + y^2) - x) / y).
epsilon2
= qscale(epsilon
, -4L);
tmp1
= qhypot(qx
, qy
, epsilon2
);
tmp2
= qatan(tmp1
, epsilon2
);
* Calculate the value of pi to within the required epsilon.
* This uses the following formula which only needs integer calculations
* except for the final operation:
* pi = 1 / SUMOF(comb(2 * N, N) ^ 3 * (42 * N + 5) / 2 ^ (12 * N + 4)),
* where the summation runs from N=0. This formula gives about 6 bits of
* accuracy per term. Since the denominator for each term is a power of two,
* we can simply use shifts to sum the terms. The combinatorial numbers
* in the formula are calculated recursively using the formula:
* comb(2*(N+1), N+1) = 2 * comb(2 * N, N) * (2 * N + 1) / N.
ZVALUE comb
; /* current combinatorial value */
ZVALUE sum
; /* current sum */
long shift
; /* current shift of result */
long N
; /* current term number */
long bits
; /* needed number of bits of precision */
if (qiszero(epsilon
) || qisneg(epsilon
))
math_error("Bad epsilon value for pi");
bits
= qprecision(epsilon
) + 4;
(void) zdivi(comb
, N
/ (3 - t
), &tmp1
);
zmuli(tmp1
, t
* (2 * N
- 1), &comb
);
zmuli(tmp2
, 42 * N
+ 5, &tmp1
);
} while ((shift
- t
) < bits
);
t1
= qscale(&qtmp
, shift
);
* Calculate the exponential function with a relative accuracy less than
NUMBER
*sum
, *term
, *qs
, *epsilon2
, *tmp
;
if (qisneg(epsilon
) || qiszero(epsilon
))
math_error("Illegal epsilon value for exp");
epsilon
= qscale(epsilon
, -4L);
* If the argument is larger than one, then divide it by a power of two
* so that it is one or less. This will make the series converge quickly.
* We will extrapolate the result for the original argument afterwards.
* Also make the argument non-negative.
scale
= zhighbit(q
->num
) - zhighbit(q
->den
) + 1;
math_error("Very large argument for exp");
tmp
= qscale(qs
, -scale
);
tmp
= qscale(epsilon
, -scale
);
epsilon2
= qscale(epsilon
, -4L);
bits
= qprecision(epsilon
) + 1;
* Now use the Taylor series expansion to calculate the exponential.
* Keep using approximations so that the fractions don't get too large.
while (qrel(term
, epsilon2
) > 0) {
term
= qdivi(tmp
, (long) n
);
tmp
= qbround(term
, bits2
);
sum
= qbround(tmp
, bits2
);
* Now repeatedly square the answer to get the final result.
* Then invert it if the original argument was negative.
sum
= qbround(tmp
, bits2
);
tmp
= qbround(sum
, bits
);
* Calculate the natural logarithm of a number accurate to the specified
NUMBER
*term
, *term2
, *sum
, *epsilon2
, *tmp1
, *tmp2
, *maxr
;
if (qisneg(q
) || qiszero(q
))
math_error("log of non-positive number");
if (qisneg(epsilon
) || qiszero(epsilon
))
math_error("Illegal epsilon for ln");
* If the number is less than one, invert it and remember that
* the result is to be negative.
if (zrel(q
->num
, q
->den
) < 0) {
k
= zhighbit(q
->num
) - zhighbit(q
->den
) + 1;
epsilon2
= qscale(epsilon
, -j
);
bits
= qprecision(epsilon
) + 1;
bits2
= qprecision(epsilon2
) + 5;
* By repeated square-roots scale number down to a value close
* to 1 so that Taylor series to be used will converge rapidly.
* The effect of scaling will be reversed by a later shift.
maxr
= iitoq(BASE
+ 1, BASE
);
while (qrel(q
, maxr
) > 0) {
tmp1
= qsqrt(q
, epsilon2
);
* Calculate a value which will always converge using the formula:
* ln((1+x)/(1-x)) = ln(1+x) - ln(1-x).
* Now use the Taylor series expansion to calculate the result.
while (qrel(term
, epsilon2
) > 0) {
tmp1
= qmul(term
, term2
);
term
= qbround(tmp1
, bits2
);
tmp1
= qdivi(term
, (long) n
);
sum
= qbround(tmp2
, bits2
);
* Calculate the final result by multiplying by the proper power
* of two to undo the square roots done at the top, and possibly
tmp1
= qscale(sum
, shift
);
sum
= qbround(tmp1
, bits
);
* Calculate the result of raising one number to the power of another.
* The result is calculated to within the specified relative error.
NUMBER
*q1
, *q2
, *epsilon
;
NUMBER
*tmp1
, *tmp2
, *epsilon2
;
epsilon2
= qscale(epsilon
, -4L);
tmp1
= qln(q1
, epsilon2
);
tmp1
= qexp(tmp2
, epsilon
);
* Calculate the Kth root of a number to within the specified accuracy.
NUMBER
*q1
, *q2
, *epsilon
;
NUMBER
*tmp1
, *tmp2
, *epsilon2
;
if (qisneg(q2
) || qiszero(q2
) || qisfrac(q2
))
math_error("Taking bad root of number");
if (qiszero(q1
) || qisone(q1
) || qisone(q2
))
return qsqrt(q1
, epsilon
);
math_error("Taking even root of negative number");
epsilon2
= qscale(epsilon
, -4L);
tmp1
= qln(q1
, epsilon2
);
tmp1
= qexp(tmp2
, epsilon
);
* Calculate the hyperbolic cosine function with a relative accuracy less
* than epsilon. This is defined by:
* cosh(x) = (exp(x) + exp(-x)) / 2.
NUMBER
*sum
, *term
, *qs
, *epsilon2
, *tmp
;
if (qisneg(epsilon
) || qiszero(epsilon
))
math_error("Illegal epsilon value for exp");
epsilon
= qscale(epsilon
, -4L);
* If the argument is larger than one, then divide it by a power of two
* so that it is one or less. This will make the series converge quickly.
* We will extrapolate the result for the original argument afterwards.
scale
= zhighbit(q
->num
) - zhighbit(q
->den
) + 1;
math_error("Very large argument for exp");
tmp
= qscale(qs
, -scale
);
tmp
= qscale(epsilon
, -scale
);
epsilon2
= qscale(epsilon
, -4L);
bits
= qprecision(epsilon
) + 1;
* Now use the Taylor series expansion to calculate the exponential.
* Keep using approximations so that the fractions don't get too large.
while (qrel(term
, epsilon2
) > 0) {
term
= qdivi(tmp
, (long) m
);
tmp
= qbround(term
, bits2
);
sum
= qbround(tmp
, bits2
);
* Now bring the number back up into range to get the final result.
* cosh(2 * x) = 2 * cosh(x)^2 - 1.
sum
= qbround(tmp
, bits2
);
tmp
= qbround(sum
, bits
);
* Calculate the hyperbolic sine with an accurary less than epsilon.
* This is calculated using the formula:
* cosh(x)^2 - sinh(x)^2 = 1.
if (qisneg(epsilon
) || qiszero(epsilon
))
math_error("Illegal epsilon value for sinh");
epsilon
= qscale(epsilon
, -4L);
tmp1
= qcosh(q
, epsilon
);
tmp2
= qsqrt(tmp1
, epsilon
);
* Calculate the hyperbolic tangent with an accurary less than epsilon.
* This is calculated using the formula:
* tanh(x) = sinh(x) / cosh(x).
NUMBER
*tmp1
, *tmp2
, *coshval
;
if (qisneg(epsilon
) || qiszero(epsilon
))
math_error("Illegal epsilon value for tanh");
epsilon
= qscale(epsilon
, -4L);
coshval
= qcosh(q
, epsilon
);
tmp2
= qsqrt(tmp1
, epsilon
);
tmp1
= qdiv(tmp2
, coshval
);
* Compute the hyperbolic arccosine within the specified accuracy.
* This is calculated using the formula:
* acosh(x) = ln(x + sqrt(x^2 - 1)).
NUMBER
*tmp1
, *tmp2
, *epsilon2
;
if (qisneg(epsilon
) || qiszero(epsilon
))
math_error("Illegal epsilon value for acosh");
math_error("Argument less than one for acosh");
epsilon2
= qscale(epsilon
, -8L);
tmp1
= qsqrt(tmp2
, epsilon2
);
tmp1
= qln(tmp2
, epsilon
);
* Compute the hyperbolic arcsine within the specified accuracy.
* This is calculated using the formula:
* asinh(x) = ln(x + sqrt(x^2 + 1)).
NUMBER
*tmp1
, *tmp2
, *epsilon2
;
if (qisneg(epsilon
) || qiszero(epsilon
))
math_error("Illegal epsilon value for asinh");
epsilon2
= qscale(epsilon
, -8L);
tmp1
= qsqrt(tmp2
, epsilon2
);
tmp1
= qln(tmp2
, epsilon
);
* Compute the hyperbolic arctangent within the specified accuracy.
* This is calculated using the formula:
* atanh(x) = ln((1 + u) / (1 - u)) / 2.
NUMBER
*tmp1
, *tmp2
, *tmp3
;
if (qisneg(epsilon
) || qiszero(epsilon
))
math_error("Illegal epsilon value for atanh");
if ((qreli(q
, 1L) > 0) || (qreli(q
, -1L) < 0))
math_error("Argument not between -1 and 1 for atanh");
tmp1
= qln(tmp3
, epsilon
);
tmp2
= qscale(tmp1
, -1L);