cmsTrig.c revision c6e579a2
1
2/*
3 * Code and supporting documentation (c) Copyright 1990 1991 Tektronix, Inc.
4 * 	All Rights Reserved
5 *
6 * This file is a component of an X Window System-specific implementation
7 * of Xcms based on the TekColor Color Management System.  Permission is
8 * hereby granted to use, copy, modify, sell, and otherwise distribute this
9 * software and its documentation for any purpose and without fee, provided
10 * that this copyright, permission, and disclaimer notice is reproduced in
11 * all copies of this software and in supporting documentation.  TekColor
12 * is a trademark of Tektronix, Inc.
13 *
14 * Tektronix makes no representation about the suitability of this software
15 * for any purpose.  It is provided "as is" and with all faults.
16 *
17 * TEKTRONIX DISCLAIMS ALL WARRANTIES APPLICABLE TO THIS SOFTWARE,
18 * INCLUDING THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
19 * PARTICULAR PURPOSE.  IN NO EVENT SHALL TEKTRONIX BE LIABLE FOR ANY
20 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
21 * RESULTING FROM LOSS OF USE, DATA, OR PROFITS, WHETHER IN AN ACTION OF
22 * CONTRACT, NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
23 * CONNECTION WITH THE USE OR THE PERFORMANCE OF THIS SOFTWARE.
24 */
25
26/*
27 *	It should be pointed out that for simplicity's sake, the
28 *	environment parameters are defined as floating point constants,
29 *	rather than octal or hexadecimal initializations of allocated
30 *	storage areas.  This means that the range of allowed numbers
31 *	may not exactly match the hardware's capabilities.  For example,
32 *	if the maximum positive double precision floating point number
33 *	is EXACTLY 1.11...E100 and the constant "MAXDOUBLE is
34 *	defined to be 1.11E100 then the numbers between 1.11E100 and
35 *	1.11...E100 are considered to be undefined.  For most
36 *	applications, this will cause no problems.
37 *
38 *	An alternate method is to allocate a global static "double" variable,
39 *	say "maxdouble", and use a union declaration and initialization
40 *	to initialize it with the proper bits for the EXACT maximum value.
41 *	This was not done because the only compilers available to the
42 *	author did not fully support union initialization features.
43 *
44 */
45
46#ifdef HAVE_CONFIG_H
47#include <config.h>
48#endif
49#include "Xcmsint.h"
50
51/* forward/static */
52static double _XcmsModulo(double value, double base);
53static double _XcmsPolynomial(
54    register int order,
55    double const *coeffs,
56    double x);
57static double
58_XcmsModuloF(
59    double val,
60    register double *dp);
61
62/*
63 *	DEFINES
64 */
65#define XCMS_MAXERROR       	0.000001
66#define XCMS_MAXITER       	10000
67#define XCMS_PI       		3.14159265358979323846264338327950
68#define XCMS_TWOPI 		6.28318530717958620
69#define XCMS_HALFPI		1.57079632679489660
70#define XCMS_FOURTHPI		0.785398163397448280
71#define XCMS_SIXTHPI		0.523598775598298820
72#define XCMS_RADIANS(d)		((d) * XCMS_PI / 180.0)
73#define XCMS_DEGREES(r)		((r) * 180.0 / XCMS_PI)
74#define XCMS_X6_UNDERFLOWS	(4.209340e-52)	/* X**6 almost underflows */
75#define XCMS_X16_UNDERFLOWS	(5.421010e-20)	/* X**16 almost underflows*/
76#define XCMS_CHAR_BIT		8
77#define XCMS_LONG_MAX		0x7FFFFFFF
78#define XCMS_DEXPLEN		11
79#define XCMS_NBITS(type)	(XCMS_CHAR_BIT * (int)sizeof(type))
80#define XCMS_FABS(x)		((x) < 0.0 ? -(x) : (x))
81
82/* XCMS_DMAXPOWTWO - largest power of two exactly representable as a double */
83#define XCMS_DMAXPOWTWO	((double)(XCMS_LONG_MAX) * \
84	    (1L << ((XCMS_NBITS(double)-XCMS_DEXPLEN) - XCMS_NBITS(int) + 1)))
85
86/*
87 *	LOCAL VARIABLES
88 */
89
90static double const cos_pcoeffs[] = {
91    0.12905394659037374438e7,
92   -0.37456703915723204710e6,
93    0.13432300986539084285e5,
94   -0.11231450823340933092e3
95};
96
97static double const cos_qcoeffs[] = {
98    0.12905394659037373590e7,
99    0.23467773107245835052e5,
100    0.20969518196726306286e3,
101    1.0
102};
103
104static double const sin_pcoeffs[] = {
105    0.20664343336995858240e7,
106   -0.18160398797407332550e6,
107    0.35999306949636188317e4,
108   -0.20107483294588615719e2
109};
110
111static double const sin_qcoeffs[] = {
112    0.26310659102647698963e7,
113    0.39270242774649000308e5,
114    0.27811919481083844087e3,
115    1.0
116};
117
118/*
119 *
120 *  FUNCTION
121 *
122 *	_XcmsCosine   double precision cosine
123 *
124 *  KEY WORDS
125 *
126 *	cos
127 *	machine independent routines
128 *	trigonometric functions
129 *	math libraries
130 *
131 *  DESCRIPTION
132 *
133 *	Returns double precision cosine of double precision
134 *	floating point argument.
135 *
136 *  USAGE
137 *
138 *	double _XcmsCosine (x)
139 *	double x;
140 *
141 *  REFERENCES
142 *
143 *	Computer Approximations, J.F. Hart et al, John Wiley & Sons,
144 *	1968, pp. 112-120.
145 *
146 *  RESTRICTIONS
147 *
148 *	The sin and cos routines are interactive in the sense that
149 *	in the process of reducing the argument to the range -PI/4
150 *	to PI/4, each may call the other.  Ultimately one or the
151 *	other uses a polynomial approximation on the reduced
152 *	argument.  The sin approximation has a maximum relative error
153 *	of 10**(-17.59) and the cos approximation has a maximum
154 *	relative error of 10**(-16.18).
155 *
156 *	These error bounds assume exact arithmetic
157 *	in the polynomial evaluation.  Additional rounding and
158 *	truncation errors may occur as the argument is reduced
159 *	to the range over which the polynomial approximation
160 *	is valid, and as the polynomial is evaluated using
161 *	finite-precision arithmetic.
162 *
163 *  PROGRAMMER
164 *
165 *	Fred Fish
166 *
167 *  INTERNALS
168 *
169 *	Computes cos(x) from:
170 *
171 *		(1)	Reduce argument x to range -PI to PI.
172 *
173 *		(2)	If x > PI/2 then call cos recursively
174 *			using relation cos(x) = -cos(x - PI).
175 *
176 *		(3)	If x < -PI/2 then call cos recursively
177 *			using relation cos(x) = -cos(x + PI).
178 *
179 *		(4)	If x > PI/4 then call sin using
180 *			relation cos(x) = sin(PI/2 - x).
181 *
182 *		(5)	If x < -PI/4 then call cos using
183 *			relation cos(x) = sin(PI/2 + x).
184 *
185 *		(6)	If x would cause underflow in approx
186 *			evaluation arithmetic then return
187 *			sqrt(1.0 - x**2).
188 *
189 *		(7)	By now x has been reduced to range
190 *			-PI/4 to PI/4 and the approximation
191 *			from HART pg. 119 can be used:
192 *
193 *			cos(x) = ( p(y) / q(y) )
194 *			Where:
195 *
196 *			y    =	x * (4/PI)
197 *
198 *			p(y) =  SUM [ Pj * (y**(2*j)) ]
199 *			over j = {0,1,2,3}
200 *
201 *			q(y) =  SUM [ Qj * (y**(2*j)) ]
202 *			over j = {0,1,2,3}
203 *
204 *			P0   =	0.12905394659037374438571854e+7
205 *			P1   =	-0.3745670391572320471032359e+6
206 *			P2   =	0.134323009865390842853673e+5
207 *			P3   =	-0.112314508233409330923e+3
208 *			Q0   =	0.12905394659037373590295914e+7
209 *			Q1   =	0.234677731072458350524124e+5
210 *			Q2   =	0.2096951819672630628621e+3
211 *			Q3   =	1.0000...
212 *			(coefficients from HART table #3843 pg 244)
213 *
214 *
215 *	**** NOTE ****    The range reduction relations used in
216 *	this routine depend on the final approximation being valid
217 *	over the negative argument range in addition to the positive
218 *	argument range.  The particular approximation chosen from
219 *	HART satisfies this requirement, although not explicitly
220 *	stated in the text.  This may not be true of other
221 *	approximations given in the reference.
222 *
223 */
224
225double _XcmsCosine(double x)
226{
227    auto double y;
228    auto double yt2;
229    double retval;
230
231    if (x < -XCMS_PI || x > XCMS_PI) {
232	x = _XcmsModulo (x, XCMS_TWOPI);
233        if (x > XCMS_PI) {
234	    x = x - XCMS_TWOPI;
235        } else if (x < -XCMS_PI) {
236	    x = x + XCMS_TWOPI;
237        }
238    }
239    if (x > XCMS_HALFPI) {
240	retval = -(_XcmsCosine (x - XCMS_PI));
241    } else if (x < -XCMS_HALFPI) {
242	retval = -(_XcmsCosine (x + XCMS_PI));
243    } else if (x > XCMS_FOURTHPI) {
244	retval = _XcmsSine (XCMS_HALFPI - x);
245    } else if (x < -XCMS_FOURTHPI) {
246	retval = _XcmsSine (XCMS_HALFPI + x);
247    } else if (x < XCMS_X6_UNDERFLOWS && x > -XCMS_X6_UNDERFLOWS) {
248	retval = _XcmsSquareRoot (1.0 - (x * x));
249    } else {
250	y = x / XCMS_FOURTHPI;
251	yt2 = y * y;
252	retval = _XcmsPolynomial (3, cos_pcoeffs, yt2) / _XcmsPolynomial (3, cos_qcoeffs, yt2);
253    }
254    return (retval);
255}
256
257
258/*
259 *  FUNCTION
260 *
261 *	_XcmsModulo   double precision modulo
262 *
263 *  KEY WORDS
264 *
265 *	_XcmsModulo
266 *	machine independent routines
267 *	math libraries
268 *
269 *  DESCRIPTION
270 *
271 *	Returns double precision modulo of two double
272 *	precision arguments.
273 *
274 *  USAGE
275 *
276 *	double _XcmsModulo (value, base)
277 *	double value;
278 *	double base;
279 *
280 *  PROGRAMMER
281 *
282 *	Fred Fish
283 *
284 */
285static double _XcmsModulo(double value, double base)
286{
287    auto double intpart;
288
289    value /= base;
290    value = _XcmsModuloF (value, &intpart);
291    value *= base;
292    return(value);
293}
294
295
296/*
297 * frac = (double) _XcmsModuloF(double val, double *dp)
298 *	return fractional part of 'val'
299 *	set *dp to integer part of 'val'
300 *
301 * Note  -> only compiled for the CA or KA.  For the KB/MC,
302 * "math.c" instantiates a copy of the inline function
303 * defined in "math.h".
304 */
305static double
306_XcmsModuloF(
307    double val,
308    register double *dp)
309{
310	register double abs;
311	/*
312	 * Don't use a register for this.  The extra precision this results
313	 * in on some systems causes problems.
314	 */
315	double ip;
316
317	/* should check for illegal values here - nan, inf, etc */
318	abs = XCMS_FABS(val);
319	if (abs >= XCMS_DMAXPOWTWO) {
320		ip = val;
321	} else {
322		ip = abs + XCMS_DMAXPOWTWO;	/* dump fraction */
323		ip -= XCMS_DMAXPOWTWO;	/* restore w/o frac */
324		if (ip > abs)		/* if it rounds up */
325			ip -= 1.0;	/* fix it */
326		ip = XCMS_FABS(ip);
327	}
328	*dp = ip;
329	return (val - ip); /* signed fractional part */
330}
331
332
333/*
334 *  FUNCTION
335 *
336 *	_XcmsPolynomial   double precision polynomial evaluation
337 *
338 *  KEY WORDS
339 *
340 *	poly
341 *	machine independent routines
342 *	math libraries
343 *
344 *  DESCRIPTION
345 *
346 *	Evaluates a polynomial and returns double precision
347 *	result.  Is passed a the order of the polynomial,
348 *	a pointer to an array of double precision polynomial
349 *	coefficients (in ascending order), and the independent
350 *	variable.
351 *
352 *  USAGE
353 *
354 *	double _XcmsPolynomial (order, coeffs, x)
355 *	int order;
356 *	double *coeffs;
357 *	double x;
358 *
359 *  PROGRAMMER
360 *
361 *	Fred Fish
362 *
363 *  INTERNALS
364 *
365 *	Evalates the polynomial using recursion and the form:
366 *
367 *		P(x) = P0 + x(P1 + x(P2 +...x(Pn)))
368 *
369 */
370
371static double _XcmsPolynomial(
372    register int order,
373    double const *coeffs,
374    double x)
375{
376    auto double rtn_value;
377
378    coeffs += order;
379    rtn_value = *coeffs--;
380    while(order-- > 0)
381	rtn_value = *coeffs-- + (x * rtn_value);
382
383    return(rtn_value);
384}
385
386
387/*
388 *  FUNCTION
389 *
390 *	_XcmsSine	double precision sine
391 *
392 *  KEY WORDS
393 *
394 *	sin
395 *	machine independent routines
396 *	trigonometric functions
397 *	math libraries
398 *
399 *  DESCRIPTION
400 *
401 *	Returns double precision sine of double precision
402 *	floating point argument.
403 *
404 *  USAGE
405 *
406 *	double _XcmsSine (x)
407 *	double x;
408 *
409 *  REFERENCES
410 *
411 *	Computer Approximations, J.F. Hart et al, John Wiley & Sons,
412 *	1968, pp. 112-120.
413 *
414 *  RESTRICTIONS
415 *
416 *	The sin and cos routines are interactive in the sense that
417 *	in the process of reducing the argument to the range -PI/4
418 *	to PI/4, each may call the other.  Ultimately one or the
419 *	other uses a polynomial approximation on the reduced
420 *	argument.  The sin approximation has a maximum relative error
421 *	of 10**(-17.59) and the cos approximation has a maximum
422 *	relative error of 10**(-16.18).
423 *
424 *	These error bounds assume exact arithmetic
425 *	in the polynomial evaluation.  Additional rounding and
426 *	truncation errors may occur as the argument is reduced
427 *	to the range over which the polynomial approximation
428 *	is valid, and as the polynomial is evaluated using
429 *	finite-precision arithmetic.
430 *
431 *  PROGRAMMER
432 *
433 *	Fred Fish
434 *
435 *  INTERNALS
436 *
437 *	Computes sin(x) from:
438 *
439 *	  (1)	Reduce argument x to range -PI to PI.
440 *
441 *	  (2)	If x > PI/2 then call sin recursively
442 *		using relation sin(x) = -sin(x - PI).
443 *
444 *	  (3)	If x < -PI/2 then call sin recursively
445 *		using relation sin(x) = -sin(x + PI).
446 *
447 *	  (4)	If x > PI/4 then call cos using
448 *		relation sin(x) = cos(PI/2 - x).
449 *
450 *	  (5)	If x < -PI/4 then call cos using
451 *		relation sin(x) = -cos(PI/2 + x).
452 *
453 *	  (6)	If x is small enough that polynomial
454 *		evaluation would cause underflow
455 *		then return x, since sin(x)
456 *		approaches x as x approaches zero.
457 *
458 *	  (7)	By now x has been reduced to range
459 *		-PI/4 to PI/4 and the approximation
460 *		from HART pg. 118 can be used:
461 *
462 *		sin(x) = y * ( p(y) / q(y) )
463 *		Where:
464 *
465 *		y    =  x * (4/PI)
466 *
467 *		p(y) =  SUM [ Pj * (y**(2*j)) ]
468 *		over j = {0,1,2,3}
469 *
470 *		q(y) =  SUM [ Qj * (y**(2*j)) ]
471 *		over j = {0,1,2,3}
472 *
473 *		P0   =  0.206643433369958582409167054e+7
474 *		P1   =  -0.18160398797407332550219213e+6
475 *		P2   =  0.359993069496361883172836e+4
476 *		P3   =  -0.2010748329458861571949e+2
477 *		Q0   =  0.263106591026476989637710307e+7
478 *		Q1   =  0.3927024277464900030883986e+5
479 *		Q2   =  0.27811919481083844087953e+3
480 *		Q3   =  1.0000...
481 *		(coefficients from HART table #3063 pg 234)
482 *
483 *
484 *	**** NOTE ****	  The range reduction relations used in
485 *	this routine depend on the final approximation being valid
486 *	over the negative argument range in addition to the positive
487 *	argument range.  The particular approximation chosen from
488 *	HART satisfies this requirement, although not explicitly
489 *	stated in the text.  This may not be true of other
490 *	approximations given in the reference.
491 *
492 */
493
494double
495_XcmsSine (double x)
496{
497    double y;
498    double yt2;
499    double retval;
500
501    if (x < -XCMS_PI || x > XCMS_PI) {
502	x = _XcmsModulo (x, XCMS_TWOPI);
503	if (x > XCMS_PI) {
504	    x = x - XCMS_TWOPI;
505	} else if (x < -XCMS_PI) {
506	    x = x + XCMS_TWOPI;
507	}
508    }
509    if (x > XCMS_HALFPI) {
510	retval = -(_XcmsSine (x - XCMS_PI));
511    } else if (x < -XCMS_HALFPI) {
512	retval = -(_XcmsSine (x + XCMS_PI));
513    } else if (x > XCMS_FOURTHPI) {
514	retval = _XcmsCosine (XCMS_HALFPI - x);
515    } else if (x < -XCMS_FOURTHPI) {
516	retval = -(_XcmsCosine (XCMS_HALFPI + x));
517    } else if (x < XCMS_X6_UNDERFLOWS && x > -XCMS_X6_UNDERFLOWS) {
518	retval = x;
519    } else {
520	y = x / XCMS_FOURTHPI;
521	yt2 = y * y;
522	retval = y * (_XcmsPolynomial (3, sin_pcoeffs, yt2) / _XcmsPolynomial(3, sin_qcoeffs, yt2));
523    }
524    return(retval);
525}
526
527
528/*
529 *	NAME
530 *		_XcmsArcTangent
531 *
532 *	SYNOPSIS
533 */
534double
535_XcmsArcTangent(double x)
536/*
537 *	DESCRIPTION
538 *		Computes the arctangent.
539 *		This is an implementation of the Gauss algorithm as
540 *		described in:
541 *		    Forman S. Acton, Numerical Methods That Work,
542 *			New York, NY, Harper & Row, 1970.
543 *
544 *	RETURNS
545 *		Returns the arctangent
546 */
547{
548    double ai, a1 = 0.0, bi, b1 = 0.0, l, d;
549    double maxerror;
550    int i;
551
552    if (x == 0.0)  {
553	return (0.0);
554    }
555    if (x < 1.0) {
556	maxerror = x * XCMS_MAXERROR;
557    } else {
558	maxerror = XCMS_MAXERROR;
559    }
560    ai = _XcmsSquareRoot( 1.0 / (1.0 + (x * x)) );
561    bi = 1.0;
562    for (i = 0; i < XCMS_MAXITER; i++) {
563	a1 = (ai + bi) / 2.0;
564	b1 = _XcmsSquareRoot((a1 * bi));
565	if (a1 == b1)
566	    break;
567	d = XCMS_FABS(a1 - b1);
568	if (d < maxerror)
569	    break;
570	ai = a1;
571	bi = b1;
572    }
573
574    l = ((a1 > b1) ? b1 : a1);
575
576    a1 = _XcmsSquareRoot(1 + (x * x));
577    return (x / (a1 * l));
578}
579