this repo has no description
1/*
2 * Copyright (c) 2002 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22 /****************************************************************************
23** File: complex.c
24**
25** Contains: C source code for implementations of floating-point
26** (double) complex functions defined in header file
27** "complex.h" for PowerPC Macintoshes in native mode.
28** Transcendental function algorithms are based on the
29** paper "Branch Cuts for Complex Elementary Functions"
30** by W. Kahan, May 17, 1987, and on Pascal and C source
31** code for the SANE 80-/96-bit extended type by Kenton
32** Hanson and Paul Finlayson, respectively.
33**
34**
35** Written by: Jon Okada, SANEitation Engineer, ext. 4-4838
36**
37** Copyright: c 1987-1993 by Apple Computer, Inc., all rights reserved.
38**
39** Change History (most recent first):
40**
41** 25 Aug 93 ali Changed clog to cLog to avoid clashing with the
42** stream i/o definition clog.
43** 14 Jul 93 ali Added #pragma fenv_access on
44** 22 Feb 93 ali Added a nomaf #pragma.
45** 05 Feb 93 JPO Modified calls to feclearexcept and feraiseexcept
46** to reflect changes in "fenv.h".
47** 18 Dec 92 JPO First created.
48**
49****************************************************************************/
50
51#pragma option nomaf
52#pragma STDC FENV_ACCESS ON
53
54#include "math.h"
55#include "complex.h"
56#include "fenv.h"
57#include "fp_private.h"
58
59#define Real(z) (__real__ z)
60#define Imag(z) (__imag__ z)
61
62/****************************************************************************
63 CONSTANTS used by complex functions
64
65#include <stdio.h>
66#include <math.h>
67#include <float.h>
68main()
69{
70
71float FPKASINHOM4f = asinhf(nextafterf(INFINITY,0.0f))/4.0f;
72float FPKTHETAf = sqrtf(nextafterf(INFINITY,0.0f))/4.0f;
73float FPKRHOf = 1.0f/FPKTHETAf;
74float FPKLOVEREf = FLT_MIN/FLT_EPSILON;
75
76printf("FPKASINHOM4 %16.7e %x\n", FPKASINHOM4f, *(int *)(&FPKASINHOM4f));
77printf("FPKTHETA %16.7e %x\n", FPKTHETAf, *(int *)(&FPKTHETAf));
78printf("FPKRHO %16.7e %x\n", FPKRHOf, *(int *)(&FPKRHOf));
79printf("FPKLOVERE %16.7e %x\n", FPKLOVEREf, *(int *)(&FPKLOVEREf));
80}
81
82****************************************************************************/
83static const /* underflow threshold / round threshold */
84 hexdouble FPKLOVERE = HEXDOUBLE(0x03600000,0x00000000);
85
86static const /* underflow threshold / round threshold */
87 hexsingle FPKLOVEREf = { 0xc000000 };
88
89static const /* exp(709.0) */
90 hexdouble FPKEXP709 = HEXDOUBLE(0x7fdd422d,0x2be5dc9b);
91
92static const /* asinh(nextafterd(+infinity,0.0))/4.0 */
93 hexdouble FPKASINHOM4 = HEXDOUBLE(0x406633ce,0x8fb9f87e);
94
95static const /* asinh(nextafterf(+infinity,0.0))/4.0 */
96 hexsingle FPKASINHOM4f = { 0x41b2d4fc };
97
98static const /* sqrt(nextafterd(+infinity,0.0))/4.0 */
99 hexdouble FPKTHETA = HEXDOUBLE(0x5fcfffff,0xffffffff);
100
101static const /* sqrt(nextafterf(+infinity,0.0))/4.0 */
102 hexsingle FPKTHETAf = { 0x5e7fffff };
103
104static const /* 4.0/sqrt(nextafterd(+infinity,0.0)) */
105 hexdouble FPKRHO = HEXDOUBLE(0x20100000,0x00000000);
106
107static const /* 4.0/sqrt(nextafterf(+infinity,0.0)) */
108 hexsingle FPKRHOf = { 0x20800001 };
109
110static
111double complex xdivc( double x, double complex y ) /* returns (real x) / (complex y) */
112{
113 double complex z;
114 double r, denom;
115
116 if ( fabs(Real(y)) >= fabs(Imag(y)) ) { /* |Real(y)| >= |Imag(y)| */
117 if (fabs(Real(y)) == INFINITY) { /* Imag(y) and Real(y) are infinite */
118 Real(z) = copysign(0.0,Real(y));
119 Imag(z) = copysign(0.0,-Imag(y));
120 }
121 else { /* |Real(y)| >= finite |Imag(y)| */
122 r = Imag(y)/Real(y);
123 denom = Real(y) + Imag(y)*r;
124 Real(z) = x/denom;
125 Imag(z) = (-x*r)/denom;
126 }
127 }
128
129 else { /* |Real(y)| !>= |Imag(y)| */
130 r = Real(y)/Imag(y);
131 denom = r*Real(y) + Imag(y);
132 Real(z) = (r*x)/denom;
133 Imag(z) = -x/denom;
134 }
135
136 return z;
137}
138
139static
140float complex xdivcf( float x, float complex y ) /* returns (real x) / (complex y) */
141{
142 float complex z;
143 float r, denom;
144
145 if ( fabsf(Real(y)) >= fabsf(Imag(y)) ) { /* |Real(y)| >= |Imag(y)| */
146 if (fabsf(Real(y)) == INFINITY) { /* Imag(y) and Real(y) are infinite */
147 Real(z) = copysignf(0.0f,Real(y));
148 Imag(z) = copysignf(0.0f,-Imag(y));
149 }
150 else { /* |Real(y)| >= finite |Imag(y)| */
151 r = Imag(y)/Real(y);
152 denom = Real(y) + Imag(y)*r;
153 Real(z) = x/denom;
154 Imag(z) = (-x*r)/denom;
155 }
156 }
157
158 else { /* |Real(y)| !>= |Imag(y)| */
159 r = Real(y)/Imag(y);
160 denom = r*Real(y) + Imag(y);
161 Real(z) = (r*x)/denom;
162 Imag(z) = -x/denom;
163 }
164
165 return z;
166}
167
168
169#if (__WANT_LONG_DOUBLE_FORMAT__ - 0L == 128L)
170
171static
172long double complex xdivcl( long double x, long double complex y ) /* returns (real x) / (complex y) */
173{
174 long double complex z;
175 long double r, denom;
176
177 if ( fabsl(Real(y)) >= fabsl(Imag(y)) ) { /* |Real(y)| >= |Imag(y)| */
178 if (fabsl(Real(y)) == INFINITY) { /* Imag(y) and Real(y) are infinite */
179 Real(z) = copysignl(0.0L,Real(y));
180 Imag(z) = copysignl(0.0L,-Imag(y));
181 }
182 else { /* |Real(y)| >= finite |Imag(y)| */
183 r = Imag(y)/Real(y);
184 denom = Real(y) + Imag(y)*r;
185 Real(z) = x/denom;
186 Imag(z) = (-x*r)/denom;
187 }
188 }
189
190 else { /* |Real(y)| !>= |Imag(y)| */
191 r = Real(y)/Imag(y);
192 denom = r*Real(y) + Imag(y);
193 Real(z) = (r*x)/denom;
194 Imag(z) = -x/denom;
195 }
196
197 return z;
198}
199#endif
200
201/****************************************************************************
202 double cabs(double complex z) returns the absolute value (magnitude) of its
203 complex argument z, avoiding spurious overflow, underflow, and invalid
204 exceptions. The algorithm is from Kahan's paper.
205
206 CONSTANTS: FPKSQT2 = sqrt(2.0) to double precision
207 FPKR2P1 = sqrt(2.0) + 1.0 to double precision
208 FPKT2P1 = sqrt(2.0) + 1.0 - FPKR2P1 to double precision, so
209 that FPKR2P1 + FPKT2P1 = sqrt(2.0) + 1.0 to double
210 double precision.
211
212 Calls: fpclassify, fabs, sqrt, feholdexcept, fesetround, feclearexcept,
213 and feupdateenv.
214****************************************************************************/
215
216extern double cabs( double complex );
217float cabsf( float complex z ){ return (float) cabs((double complex) z); }
218
219#if (__WANT_LONG_DOUBLE_FORMAT__ - 0L == 128L)
220
221static const long double FPKEXP709L = 8.2184074615549721892413723865978e+307L;
222
223#define M_FPKT2P1Q -.0000000000000000000000000000000000014303281246230519268233202620092676L
224
225long double cabsl ( long double complex z )
226{
227 long double a,b,s,t;
228 fenv_t env;
229 long double FPR_inf = INFINITY;
230
231 a = fabsl(Real(z));
232 b = fabsl(Imag(z));
233
234 if (unlikely( (a == FPR_inf) || (b == FPR_inf) ))
235 return FPR_inf;
236
237 if (unlikely( (a != a) || (b != b) ))
238 return __FABS ( a + b );
239
240 if (unlikely((a == 0.0L) || (b == 0.0L) ))
241 return __FABS ( a + b );
242
243 /* both components of z are finite, nonzero */
244 {
245 (void)feholdexcept(&env); /* save environment, clear flags */
246 (void)fesetround(FE_TONEAREST); /* set default rounding */
247
248 s = 0.0L;
249 if (a < b) /* order a >= b */
250 {
251 t = a;
252 a = b;
253 b = t;
254 }
255 t = a - b; /* magnitude difference */
256
257 if (t != a) /* b not negligible relative to a */
258 {
259 if (t > b) /* a - b > b */
260 {
261 s = a/b;
262 s += sqrtl(1.0L + s*s);
263 }
264 else /* a - b <= b */
265 {
266 s = t/b;
267 t = (2.0L + s)*s;
268 s = ((M_FPKT2P1Q+t/(M_SQRT2+sqrt(2.0L+t)))+s)+(1.0L + M_SQRT2);
269 }
270
271 s = b/s; /* may spuriously underflow */
272 feclearexcept(FE_UNDERFLOW);
273 }
274
275 feupdateenv(&env); /* restore environment */
276 return (a + s); /* deserved overflow occurs here */
277 } /* finite, nonzero case */
278}
279#endif
280
281/****************************************************************************
282 double carg(double complex z) returns the argument (in radians) of its
283 complex argument z. The algorithm is from Kahan's paper.
284
285 The argument of a complex number z = x + i*y is defined as atan2(y,x)
286 for finite x and y.
287
288 CONSTANTS: FPKPI2 = pi/2.0 to double precision
289 FPKPI = pi to double precision
290
291 Calls: fpclassify, copysign, fabs, atan
292****************************************************************************/
293
294double carg ( double complex z )
295{
296 double a,b,argr;
297 int clre,clim;
298
299 a = Real(z);
300 b = Imag(z);
301 clre = fpclassify(a);
302 clim = fpclassify(b);
303
304 if ((clre == FP_ZERO) && (clim == FP_ZERO)) { /* zero real and imag parts */
305 a = copysign(1.0, a);
306 }
307
308 if ((clre == FP_INFINITE) && (clim == FP_INFINITE)) { /* both parts INF */
309 a = copysign(1.0, a);
310 b = copysign(1.0, b);
311 }
312
313 if (fabs(b) > fabs(a)) /* |imag| > |real| */
314 argr = copysign(M_PI_2, b) - atan(a/b);
315
316 else {
317 if (a < 0.0) /* |real| >= |imag| */
318 argr = copysign(M_PI, b) + atan(b/a);
319 else
320 argr = atan(b/a);
321 }
322
323 return argr;
324}
325
326float cargf ( float complex z )
327{
328 float a,b,argr;
329 int clre,clim;
330
331 a = Real(z);
332 b = Imag(z);
333 clre = fpclassify(a);
334 clim = fpclassify(b);
335
336 if ((clre == FP_ZERO) && (clim == FP_ZERO)) { /* zero real and imag parts */
337 a = copysignf(1.0f, a);
338 }
339
340 if ((clre == FP_INFINITE) && (clim == FP_INFINITE)) { /* both parts INF */
341 a = copysignf(1.0f, a);
342 b = copysignf(1.0f, b);
343 }
344
345 if (fabsf(b) > fabsf(a)) /* |imag| > |real| */
346 argr = copysignf((float) M_PI_2, b) - atanf(a/b);
347
348 else {
349 if (a < 0.0f) /* |real| >= |imag| */
350 argr = copysignf((float) M_PI, b) + atanf(b/a);
351 else
352 argr = atanf(b/a);
353 }
354
355 return argr;
356}
357
358#if (__WANT_LONG_DOUBLE_FORMAT__ - 0L == 128L)
359
360long double cargl ( long double complex z )
361{
362 long double a,b,argr;
363 int clre,clim;
364
365 a = Real(z);
366 b = Imag(z);
367 clre = fpclassify(a);
368 clim = fpclassify(b);
369
370 if ((clre == FP_ZERO) && (clim == FP_ZERO)) { /* zero real and imag parts */
371 a = copysignl(1.0L, a);
372 }
373
374 if ((clre == FP_INFINITE) && (clim == FP_INFINITE)) { /* both parts INF */
375 a = copysignl(1.0L, a);
376 b = copysignl(1.0L, b);
377 }
378
379 if (fabsl(b) > fabsl(a)) /* |imag| > |real| */
380 argr = copysignl(M_PI_2, b) - atanl(a/b);
381
382 else {
383 if (a < 0.0L) /* |real| >= |imag| */
384 argr = copysignl(M_PI, b) + atanl(b/a);
385 else
386 argr = atanl(b/a);
387 }
388
389 return argr;
390}
391#endif
392
393/****************************************************************************
394 static double cssqs(double complex z, long int *k) returns rho = |z/(2^*k)|^2
395 with scale factor *k set to avoid overflow/underflow. Algorithm is
396 from the Kahan paper.
397
398 CONSTANTS: FPKINF = +infinity
399 FPKLOVERE = (double underflow threshold)/(double round threshold)
400 = (4.0*nextafterd(1.0,0.0)/nextafterd(INF,0.0))
401 /(1.0 - nextafterd(1.0,0)
402
403 Calls: fabs, logb, scalb, fmax, feholdexcept, fetestexcept, feclearexcept,
404 and feupdateenv.
405
406 Called by: csqrt and clog.
407****************************************************************************/
408
409static double cssqs ( double complex z, int *k)
410{
411 double a,b,rho;
412 fenv_t env;
413 int iscale;
414
415 iscale = 0;
416 a = fabs(Real(z));
417 b = fabs(Imag(z));
418 (void)feholdexcept(&env); /* save environment, clr flags */
419 rho = a*a + b*b; /* preliminary result */
420
421 if ((a == INFINITY) || (b == INFINITY)) {
422 rho = INFINITY; /* +INF result if Real(z) or Imag(z) is infinite */
423 }
424
425 else if (fetestexcept(FE_OVERFLOW) || (fetestexcept(FE_UNDERFLOW) && (rho < FPKLOVERE.d))) {
426 iscale = ilogb(fmax(a,b)); /* scaling necessary */
427 a = scalbn(a,-iscale);
428 b = scalbn(b,-iscale);
429 rho = a*a + b*b; /* re-calculate scaled square magnitude */
430 }
431
432 feclearexcept(FE_OVERFLOW + FE_UNDERFLOW);
433 feupdateenv(&env); /* restore environment */
434 *k = iscale; /* store scale value */
435 return (rho);
436}
437
438static float cssqsf ( float complex z, int *k)
439{
440 float a,b,rho;
441 fenv_t env;
442 int iscale;
443
444 iscale = 0;
445 a = fabsf(Real(z));
446 b = fabsf(Imag(z));
447 (void)feholdexcept(&env); /* save environment, clr flags */
448 rho = a*a + b*b; /* preliminary result */
449
450 if ((a == INFINITY) || (b == INFINITY)) {
451 rho = INFINITY; /* +INF result if Real(z) or Imag(z) is infinite */
452 }
453
454 else if (fetestexcept(FE_OVERFLOW) || (fetestexcept(FE_UNDERFLOW) && (rho < FPKLOVEREf.fval))) {
455 iscale = logbf(fmaxf(a,b)); /* scaling necessary */
456 a = scalbnf(a,-iscale);
457 b = scalbnf(b,-iscale);
458 rho = a*a + b*b; /* re-calculate scaled square magnitude */
459 }
460
461 feclearexcept(FE_OVERFLOW + FE_UNDERFLOW);
462 feupdateenv(&env); /* restore environment */
463 *k = iscale; /* store scale value */
464 return (rho);
465}
466
467#if (__WANT_LONG_DOUBLE_FORMAT__ - 0L == 128L)
468
469static long double cssqsl ( long double complex z, int *k)
470{
471 long double a,b,rho;
472 fenv_t env;
473 int iscale;
474
475 iscale = 0;
476 a = fabsl(Real(z));
477 b = fabsl(Imag(z));
478 (void)feholdexcept(&env); /* save environment, clr flags */
479 rho = a*a + b*b; /* preliminary result */
480
481 if ((a == INFINITY) || (b == INFINITY)) {
482 rho = INFINITY; /* +INF result if Real(z) or Imag(z) is infinite */
483 }
484
485 else if (fetestexcept(FE_OVERFLOW) || (fetestexcept(FE_UNDERFLOW) && (rho < FPKLOVERE.d))) {
486 iscale = logbl(fmaxl(a,b)); /* scaling necessary */
487 a = scalbnl(a,-iscale);
488 b = scalbnl(b,-iscale);
489 rho = a*a + b*b; /* re-calculate scaled square magnitude */
490 }
491
492 feclearexcept(FE_OVERFLOW + FE_UNDERFLOW);
493 feupdateenv(&env); /* restore environment */
494 *k = iscale; /* store scale value */
495 return (rho);
496}
497#endif
498
499/****************************************************************************
500 double complex csqrt(double complex z) returns the complex square root of its argument.
501 The algorithm, which is from the Kahan paper, uses the following
502 identities:
503
504 sqrt(x + i*y) = sqrt((|z| + Real(z))/2) + i*sqrt((|z| - Real(z))/2) and
505 sqrt(x - i*y) = sqrt((|z| + Real(z))/2) - i*sqrt((|z| - Real(z))/2),
506
507 where y is positive and x may be positive or negative.
508
509 CONSTANTS: FPKINF = +infinity
510
511 Calls: cssqs, scalb, fabs, sqrt, copysign.
512****************************************************************************/
513
514double complex csqrt ( double complex z )
515{
516 double rho,x,y;
517 double complex w;
518 int k;
519
520 rho = cssqs(z,&k); /* scaled square magnitude */
521
522 if (Real(z) == Real(z))
523 rho = scalbn(fabs(Real(z)),-k) + sqrt(rho); /* scaled |Real(z)| + |z| */
524
525 if (k%2) /* k is odd */
526 k = (k-1)/2;
527 else { /* k is even */
528 k = k/2 - 1;
529 rho = rho + rho;
530 }
531
532 rho = scalbn(sqrt(rho),k); /* sqrt((|Real(z)| + |z|)/2) */
533 x = rho;
534 y = Imag(z);
535
536 if (rho != 0.0) {
537 if (fabs(y) != INFINITY)
538 y = (y/rho)*0.5; /* signbit(Imag(z))*sqrt((|z|-|Real(z)|)/2 */
539 if (Real(z) < 0.0) {
540 x = fabs(y);
541 y = copysign(rho,Imag(z));
542 }
543 }
544
545 Real(w) = x;
546 Imag(w) = y;
547 return w;
548}
549
550float complex csqrtf ( float complex z )
551{
552 float rho,x,y;
553 float complex w;
554 int k;
555
556 rho = cssqsf(z,&k); /* scaled square magnitude */
557
558 if (Real(z) == Real(z))
559 rho = scalbnf(fabsf(Real(z)),-k) + sqrtf(rho); /* scaled |Real(z)| + |z| */
560
561 if (k%2) /* k is odd */
562 k = (k-1)/2;
563 else { /* k is even */
564 k = k/2 - 1;
565 rho = rho + rho;
566 }
567
568 rho = scalbnf(sqrtf(rho),k); /* sqrt((|Real(z)| + |z|)/2) */
569 x = rho;
570 y = Imag(z);
571
572 if (rho != 0.0f) {
573 if (fabsf(y) != INFINITY)
574 y = (y/rho)*0.5f; /* signbit(Imag(z))*sqrt((|z|-|Real(z)|)/2 */
575 if (Real(z) < 0.0f) {
576 x = fabsf(y);
577 y = copysignf(rho,Imag(z));
578 }
579 }
580
581 Real(w) = x;
582 Imag(w) = y;
583 return w;
584}
585
586#if (__WANT_LONG_DOUBLE_FORMAT__ - 0L == 128L)
587
588long double complex csqrtl ( long double complex z )
589{
590 long double rho,x,y;
591 long double complex w;
592 int k;
593
594 rho = cssqsl(z,&k); /* scaled square magnitude */
595
596 if (Real(z) == Real(z))
597 rho = scalbnl(fabsl(Real(z)),-k) + sqrtl(rho); /* scaled |Real(z)| + |z| */
598
599 if (k%2) /* k is odd */
600 k = (k-1)/2;
601 else { /* k is even */
602 k = k/2 - 1;
603 rho = rho + rho;
604 }
605
606 rho = scalbnl(sqrtl(rho),k); /* sqrt((|Real(z)| + |z|)/2) */
607 x = rho;
608 y = Imag(z);
609
610 if (rho != 0.0L) {
611 if (fabsl(y) != INFINITY)
612 y = (y/rho)*0.5L; /* signbit(Imag(z))*sqrt((|z|-|Real(z)|)/2 */
613 if (Real(z) < 0.0L) {
614 x = fabsl(y);
615 y = copysignl(rho,Imag(z));
616 }
617 }
618
619 Real(w) = x;
620 Imag(w) = y;
621 return w;
622}
623#endif
624
625/****************************************************************************
626 double complex clog(double complex z) returns the complex natural logarithm of its
627 argument. The algorithm, which is from the Kahan paper, avoids spurious
628 underflow or overflow.
629
630 CONSTANTS: FPKSQRTHALF = sqrt(0.5) to double precision
631 FPKLOG2 = log(2.0) to double precision
632
633 Calls: fabs, cssqs, log1p, log, carg.
634****************************************************************************/
635
636double complex clog ( double complex z )
637{
638 double rho,dmax,dmin,temp;
639 double complex w;
640 int k;
641
642 dmax = fabs(Real(z)); /* order real and imaginary parts of z by magnitude */
643 dmin = fabs(Imag(z));
644 if (dmax < dmin) {
645 temp = dmax;
646 dmax = dmin;
647 dmin = temp;
648 }
649
650 rho = cssqs(z,&k); /* scaled |z*z| */
651
652 if ((k == 0) && (dmax > M_SQRT1_2) && ((dmax <= 1.25) || (rho < 3.0)))
653 Real(w) = log1p((dmax - 1.0)*(dmax + 1.0) + dmin*dmin)*0.5; /* |z| near 1.0 */
654 else
655 Real(w) = log(rho)*0.5 + k*M_LN2; /* more naive approximation */
656
657 Imag(w) = carg(z); /* imaginary part of logarithm */
658
659 return w;
660}
661
662float complex clogf ( float complex z )
663{
664 float rho,dmax,dmin,temp;
665 float complex w;
666 int k;
667
668 dmax = fabsf(Real(z)); /* order real and imaginary parts of z by magnitude */
669 dmin = fabsf(Imag(z));
670 if (dmax < dmin) {
671 temp = dmax;
672 dmax = dmin;
673 dmin = temp;
674 }
675
676 rho = cssqsf(z,&k); /* scaled |z*z| */
677
678 if ((k == 0) && (dmax > M_SQRT1_2) && ((dmax <= 1.25f) || (rho < 3.0f)))
679 Real(w) = log1pf((dmax - 1.0f)*(dmax + 1.0f) + dmin*dmin)*0.5f; /* |z| near 1.0 */
680 else
681 Real(w) = logf(rho)*0.5f + (float)((double)k*M_LN2); /* more naive approximation */
682
683 Imag(w) = cargf(z); /* imaginary part of logarithm */
684
685 return w;
686}
687
688#if (__WANT_LONG_DOUBLE_FORMAT__ - 0L == 128L)
689
690long double complex clogl ( long double complex z )
691{
692 long double rho,dmax,dmin,temp;
693 long double complex w;
694 int k;
695
696 dmax = fabsl(Real(z)); /* order real and imaginary parts of z by magnitude */
697 dmin = fabsl(Imag(z));
698 if (dmax < dmin) {
699 temp = dmax;
700 dmax = dmin;
701 dmin = temp;
702 }
703
704 rho = cssqsl(z,&k); /* scaled |z*z| */
705
706 if ((k == 0) && (dmax > M_SQRT1_2) && ((dmax <= 1.25L) || (rho < 3.0L)))
707 Real(w) = log1pl((dmax - 1.0L)*(dmax + 1.0L) + dmin*dmin)*0.5L; /* |z| near 1.0 */
708 else
709 Real(w) = logl(rho)*0.5L + k*M_LN2; /* more naive approximation */
710
711 Imag(w) = cargl(z); /* imaginary part of logarithm */
712
713 return w;
714}
715#endif
716
717/****************************************************************************
718 static double coshmul(double x, double y) returns y*cosh(x) while
719 avoiding spurious overflow and invalid exceptions.
720
721 CONSTANTS: FPKEXP709 = exp(709.0) in double precision
722
723 Calls: cosh, exp, fpclassify, fabs, and scalb.
724
725 Called by: csin, ccos, csinh, and ccosh.
726****************************************************************************/
727
728static double coshmul ( double x, double y )
729{
730 double absx, result;
731
732 absx = fabs(x);
733 if (absx <= 709.0) { /* cosh(x) is finite */
734 return y*cosh(x);
735 }
736
737 else if (fpclassify(x) < FP_ZERO) { /* x is NaN or infinite */
738 return (y*absx);
739 }
740
741 else if (absx > 1460.0) { /* probable overflow case */
742 return (scalbn(y,2100));
743 }
744
745 else { /* cosh(x) overflows but y*cosh(x) may not */
746 result = (0.5 * FPKEXP709.d) * y; /* initialize result to cosh(709) */
747 absx -= 709.0; /* reduce exponential argument */
748 while (absx > 709.0) { /* exponential reduction loop */
749 result *= FPKEXP709.d;
750 absx -= 709.0;
751 }
752 return (result*exp(absx)); /* final multiplication */
753 }
754}
755
756static float coshmulf ( float x, float y )
757{
758 float absx;
759
760 absx = fabsf(x);
761 if (absx <= 89.0f) { /* coshf(x) is finite */
762 return y*coshf(x);
763 }
764
765 else if (fpclassify(x) < FP_ZERO) { /* x is NaN or infinite */
766 return (y*absx);
767 }
768
769 return (float)coshmul((double)x, (double)y);
770}
771
772// long double has same range as double
773#if (__WANT_LONG_DOUBLE_FORMAT__ - 0L == 128L)
774
775static long double coshmull ( long double x, long double y )
776{
777 long double absx, result;
778
779 absx = fabsl(x);
780 if (absx <= 709.0L) { /* cosh(x) is finite */
781 return y*coshl(x);
782 }
783
784 else if (fpclassify(x) < FP_ZERO) { /* x is NaN or infinite */
785 return (y*absx);
786 }
787
788 else if (absx > 1460.0L) { /* probable overflow case */
789 return (scalbnl(y,2100));
790 }
791
792 else { /* cosh(x) overflows but y*cosh(x) may not */
793 long double t = FPKEXP709L;
794
795 result = (0.5L * t) * y; /* initialize result to cosh(709) */
796 absx -= 709.0L; /* reduce exponential argument */
797 while (absx > 709.0L) { /* exponential reduction loop */
798 result *= t;
799 absx -= 709.0L;
800 }
801 return (result*expl(absx)); /* final multiplication */
802 }
803}
804#endif
805
806/****************************************************************************
807 static double sinhmul(double x, double y) returns y*sinh(x) while
808 avoiding spurious overflow and invalid exceptions.
809
810 CONSTANTS: FPKEXP709 = exp(709.0) in double precision
811
812 Calls: sinh, exp, fpclassify, fabs, and scalb.
813
814 Called by: csin, ccos, csinh, and ccosh.
815****************************************************************************/
816
817static double sinhmul ( double x, double y )
818{
819 double absx, result;
820
821 absx = fabs(x);
822 if (absx <= 709.0) { /* sinh(x) is finite */
823 return y*sinh(x);
824 }
825
826 else if (fpclassify(x) < FP_ZERO) { /* x is NaN or infinite */
827 return (y*x);
828 }
829
830 else if (absx > 1460.0) { /* probable overflow case */
831 return (scalbn(y,2100));
832 }
833
834 else { /* sinh(x) overflows but y*sinh(x) may not */
835 result = (0.5*FPKEXP709.d)*y; /* initialize result to |sinh(709)| */
836 absx -= 709.0; /* reduce exponential argument */
837 if (signbit(x) != 0) result = -result; /* take care of sign of result */
838 while (absx > 709.0) { /* exponential reduction loop */
839 result *= FPKEXP709.d;
840 absx -= 709.0;
841 }
842 return (result*exp(absx)); /* final multiplication */
843 }
844}
845
846static float sinhmulf ( float x, float y )
847{
848 float absx;
849
850 absx = fabsf(x);
851 if (absx <= 709.0f) { /* sinhf(x) is finite */
852 return y*sinhf(x);
853 }
854
855 else if (fpclassify(x) < FP_ZERO) { /* x is NaN or infinite */
856 return (y*x);
857 }
858
859 return (float)sinhmul((double)x, (double)y);
860}
861
862#if (__WANT_LONG_DOUBLE_FORMAT__ - 0L == 128L)
863
864static long double sinhmull ( long double x, long double y )
865{
866 long double absx, result;
867
868 absx = fabsl(x);
869 if (absx <= 709.0L) { /* sinh(x) is finite */
870 return y*sinhl(x);
871 }
872
873 else if (fpclassify(x) < FP_ZERO) { /* x is NaN or infinite */
874 return (y*x);
875 }
876
877 else if (absx > 1460.0L) { /* probable overflow case */
878 return (scalbnl(y,2100));
879 }
880
881 else { /* sinh(x) overflows but y*sinh(x) may not */
882 long double t = FPKEXP709L;
883
884 result = (0.5L*t)*y; /* initialize result to y*|sinh(709)| */
885 absx -= 709.0L; /* reduce exponential argument */
886 if (signbit(x) != 0) result = -result; /* take care of sign of result */
887 while (absx > 709.0L) { /* exponential reduction loop */
888 result *= t;
889 absx -= 709.0L;
890 }
891 return (result*expl(absx)); /* final multiplication */
892 }
893}
894#endif
895
896
897/****************************************************************************
898 double complex csin(double complex z) returns the complex sine of its argument. The
899 algorithm is based upon the identity:
900
901 sin(x + i*y) = sin(x)*cosh(y) + i*cos(x)*sinh(y).
902
903 Signaling of spurious overflows, underflows, and invalids is avoided where
904 possible.
905
906 Calls: sin, cos, coshmul, sinhmul, feholdexcept, feclearexcept, and
907 feupdateenv.
908****************************************************************************/
909
910double complex csin ( double complex z )
911{
912 fenv_t env;
913 double sinval, cosval;
914 double complex w;
915
916 (void)feholdexcept(&env); /* save environment, clear flags */
917 sinval = sin(Real(z)); /* sine of real part of argument */
918 cosval = cos(Real(z)); /* cosine of real part of argument */
919 Real(w) = coshmul(Imag(z),sinval); /* real result = sinval*cosh(Imag(z)) */
920 Imag(w) = sinhmul(Imag(z),cosval); /* imag result = cosval*sinh(Imag(z)) */
921 feclearexcept(FE_UNDERFLOW); /* underflows don't matter */
922 feupdateenv(&env); /* update environment */
923 return w;
924}
925
926float complex csinf ( float complex z )
927{
928 fenv_t env;
929 float sinval, cosval;
930 float complex w;
931
932 (void)feholdexcept(&env); /* save environment, clear flags */
933 sinval = sinf(Real(z)); /* sine of real part of argument */
934 cosval = cosf(Real(z)); /* cosine of real part of argument */
935 Real(w) = coshmulf(Imag(z),sinval); /* real result = sinval*cosh(Imag(z)) */
936 Imag(w) = sinhmulf(Imag(z),cosval); /* imag result = cosval*sinh(Imag(z)) */
937 feclearexcept(FE_UNDERFLOW); /* underflows don't matter */
938 feupdateenv(&env); /* update environment */
939 return w;
940}
941
942#if (__WANT_LONG_DOUBLE_FORMAT__ - 0L == 128L)
943
944long double complex csinl ( long double complex z )
945{
946 fenv_t env;
947 long double sinval, cosval;
948 long double complex w;
949
950 (void)feholdexcept(&env); /* save environment, clear flags */
951 sinval = sinl(Real(z)); /* sine of real part of argument */
952 cosval = cosl(Real(z)); /* cosine of real part of argument */
953 Real(w) = coshmull(Imag(z),sinval); /* real result = sinval*cosh(Imag(z)) */
954 Imag(w) = sinhmull(Imag(z),cosval); /* imag result = cosval*sinh(Imag(z)) */
955 feclearexcept(FE_UNDERFLOW); /* underflows don't matter */
956 feupdateenv(&env); /* update environment */
957 return w;
958}
959#endif
960
961/****************************************************************************
962 double complex ccos(double complex z) returns the complex cosine of its argument. The
963 algorithm is based upon the identity:
964
965 cos(x + i*y) = cos(x)*cosh(y) - i*sin(x)*sinh(y).
966
967 Signaling of spurious overflows, underflows, and invalids is avoided where
968 possible.
969
970 Calls: sin, cos, coshmul, sinhmul, feholdexcept, feclearexcept, and
971 feupdateenv.
972****************************************************************************/
973
974double complex ccos ( double complex z )
975{
976 fenv_t env;
977 double sinval, cosval;
978 double complex w;
979
980 (void)feholdexcept(&env); /* save environment, clear flags */
981 sinval = sin(Real(z)); /* sine of real part of argument */
982 cosval = cos(Real(z)); /* cosine of real part of argument */
983 Real(w) = coshmul(Imag(z),cosval); /* real result = cosval*cosh(Imag(z)) */
984 Imag(w) = sinhmul(Imag(z),-sinval); /* imag result = -sinval*sinh(Imag(z)) */
985 feclearexcept(FE_UNDERFLOW); /* underflows don't matter */
986 feupdateenv(&env); /* update environment */
987 return w;
988}
989
990float complex ccosf ( float complex z )
991{
992 fenv_t env;
993 float sinval, cosval;
994 float complex w;
995
996 (void)feholdexcept(&env); /* save environment, clear flags */
997 sinval = sinf(Real(z)); /* sine of real part of argument */
998 cosval = cosf(Real(z)); /* cosine of real part of argument */
999 Real(w) = coshmulf(Imag(z),cosval); /* real result = cosval*cosh(Imag(z)) */
1000 Imag(w) = sinhmulf(Imag(z),-sinval); /* imag result = -sinval*sinh(Imag(z)) */
1001 feclearexcept(FE_UNDERFLOW); /* underflows don't matter */
1002 feupdateenv(&env); /* update environment */
1003 return w;
1004}
1005
1006#if (__WANT_LONG_DOUBLE_FORMAT__ - 0L == 128L)
1007
1008long double complex ccosl ( long double complex z )
1009{
1010 fenv_t env;
1011 long double sinval, cosval;
1012 long double complex w;
1013
1014 (void)feholdexcept(&env); /* save environment, clear flags */
1015 sinval = sinl(Real(z)); /* sine of real part of argument */
1016 cosval = cosl(Real(z)); /* cosine of real part of argument */
1017 Real(w) = coshmull(Imag(z),cosval); /* real result = cosval*cosh(Imag(z)) */
1018 Imag(w) = sinhmull(Imag(z),-sinval); /* imag result = -sinval*sinh(Imag(z)) */
1019 feclearexcept(FE_UNDERFLOW); /* underflows don't matter */
1020 feupdateenv(&env); /* update environment */
1021 return w;
1022}
1023#endif
1024
1025
1026/****************************************************************************
1027 double complex csinh(double complex z) returns the complex hyperbolic sine of its
1028 argument. The algorithm is based upon the identity:
1029
1030 sinh(x + i*y) = cos(y)*sinh(x) + i*sin(y)*cosh(x).
1031
1032 Signaling of spurious overflows, underflows, and invalids is avoided where
1033 possible.
1034
1035 Calls: sin, cos, coshmul, sinhmul, feholdexcept, feclearexcept, and
1036 feupdateenv.
1037****************************************************************************/
1038
1039double complex csinh ( double complex z )
1040{
1041 fenv_t env;
1042 double sinval, cosval;
1043 double complex w;
1044
1045 (void)feholdexcept(&env); /* save environment, clear flags */
1046 sinval = sin(Imag(z)); /* sine of imaginary part of argument */
1047 cosval = cos(Imag(z)); /* cosine of imaginary part of argument */
1048 Real(w) = sinhmul(Real(z),cosval); /* real result = cosval*sinh(Real(z)) */
1049 Imag(w) = coshmul(Real(z),sinval); /* imag result = sinval*cosh(Real(z)) */
1050 feclearexcept(FE_UNDERFLOW); /* underflows don't matter */
1051 feupdateenv(&env); /* update environment */
1052 return w;
1053}
1054
1055float complex csinhf ( float complex z )
1056{
1057 fenv_t env;
1058 float sinval, cosval;
1059 float complex w;
1060
1061 (void)feholdexcept(&env); /* save environment, clear flags */
1062 sinval = sinf(Imag(z)); /* sine of imaginary part of argument */
1063 cosval = cosf(Imag(z)); /* cosine of imaginary part of argument */
1064 Real(w) = sinhmulf(Real(z),cosval); /* real result = cosval*sinh(Real(z)) */
1065 Imag(w) = coshmulf(Real(z),sinval); /* imag result = sinval*cosh(Real(z)) */
1066 feclearexcept(FE_UNDERFLOW); /* underflows don't matter */
1067 feupdateenv(&env); /* update environment */
1068 return w;
1069}
1070
1071#if (__WANT_LONG_DOUBLE_FORMAT__ - 0L == 128L)
1072
1073long double complex csinhl ( long double complex z )
1074{
1075 fenv_t env;
1076 long double sinval, cosval;
1077 long double complex w;
1078
1079 (void)feholdexcept(&env); /* save environment, clear flags */
1080 sinval = sinl(Imag(z)); /* sine of imaginary part of argument */
1081 cosval = cosl(Imag(z)); /* cosine of imaginary part of argument */
1082 Real(w) = sinhmull(Real(z),cosval); /* real result = cosval*sinh(Real(z)) */
1083 Imag(w) = coshmull(Real(z),sinval); /* imag result = sinval*cosh(Real(z)) */
1084 feclearexcept(FE_UNDERFLOW); /* underflows don't matter */
1085 feupdateenv(&env); /* update environment */
1086 return w;
1087}
1088#endif
1089
1090
1091/****************************************************************************
1092 double complex ccosh(double complex z) returns the complex hyperbolic cosine of its
1093 argument. The algorithm is based upon the identity:
1094
1095 cosh(x + i*y) = cos(y)*cosh(x) + i*sin(y)*sinh(x).
1096
1097 Signaling of spurious overflows, underflows, and invalids is avoided where
1098 possible.
1099
1100 Calls: sin, cos, coshmul, sinhmul, feholdexcept, feclearexcept, and
1101 feupdateenv.
1102****************************************************************************/
1103
1104double complex ccosh ( double complex z )
1105{
1106 fenv_t env;
1107 double sinval, cosval;
1108 double complex w;
1109
1110 (void)feholdexcept(&env); /* save environment, clear flags */
1111 sinval = sin(Imag(z)); /* sine of imaginary part of argument */
1112 cosval = cos(Imag(z)); /* cosine of imaginary part of argument */
1113 Real(w) = coshmul(Real(z),cosval); /* real result = cosval*cosh(Real(z)) */
1114 Imag(w) = sinhmul(Real(z),sinval); /* imag result = sinval*sinh(Real(z)) */
1115 feclearexcept(FE_UNDERFLOW); /* underflows don't matter */
1116 feupdateenv(&env); /* update environment */
1117 return w;
1118}
1119
1120float complex ccoshf ( float complex z )
1121{
1122 fenv_t env;
1123 float sinval, cosval;
1124 float complex w;
1125
1126 (void)feholdexcept(&env); /* save environment, clear flags */
1127 sinval = sinf(Imag(z)); /* sine of imaginary part of argument */
1128 cosval = cosf(Imag(z)); /* cosine of imaginary part of argument */
1129 Real(w) = coshmulf(Real(z),cosval); /* real result = cosval*cosh(Real(z)) */
1130 Imag(w) = sinhmulf(Real(z),sinval); /* imag result = sinval*sinh(Real(z)) */
1131 feclearexcept(FE_UNDERFLOW); /* underflows don't matter */
1132 feupdateenv(&env); /* update environment */
1133 return w;
1134}
1135
1136#if (__WANT_LONG_DOUBLE_FORMAT__ - 0L == 128L)
1137
1138long double complex ccoshl ( long double complex z )
1139{
1140 fenv_t env;
1141 long double sinval, cosval;
1142 long double complex w;
1143
1144 (void)feholdexcept(&env); /* save environment, clear flags */
1145 sinval = sinl(Imag(z)); /* sine of imaginary part of argument */
1146 cosval = cosl(Imag(z)); /* cosine of imaginary part of argument */
1147 Real(w) = coshmull(Real(z),cosval); /* real result = cosval*cosh(Real(z)) */
1148 Imag(w) = sinhmull(Real(z),sinval); /* imag result = sinval*sinh(Real(z)) */
1149 feclearexcept(FE_UNDERFLOW); /* underflows don't matter */
1150 feupdateenv(&env); /* update environment */
1151 return w;
1152}
1153#endif
1154
1155
1156/****************************************************************************
1157 double complex cexp(double complex z) returns the complex exponential of its
1158 argument. The algorithm is based upon the identity:
1159
1160 exp(x + i*y) = cos(y)*exp(x) + i*sin(y)*exp(x).
1161
1162 Signaling of spurious overflows and invalids is avoided where
1163 possible.
1164
1165 CONSTANTS: FPKEXP709 = exp(709.0) to double precision
1166
1167 Calls: sin, cos, exp, fpclassify, and scalb.
1168
1169 Called by: cepwry, cxpwri, cxpwre, and cxpwry
1170****************************************************************************/
1171
1172double complex cexp ( double complex z )
1173{
1174 double sinval, cosval, expval, exparg;
1175 double complex w;
1176
1177 sinval = sin(Imag(z)); /* sine of imaginary part of argument */
1178 cosval = cos(Imag(z)); /* cosine of imaginary part of argument */
1179
1180 if (Real(z) <= 709.0) { /* exp(Real(z)) is finite */
1181 expval = exp(Real(z)); /* evaluate exponential */
1182 Real(w) = cosval*expval; /* real result = cos(Imag(z))*exp(Real(z)) */
1183 Imag(w) = sinval*expval; /* imag result = sin(Imag(z))*exp(Real(z)) */
1184 }
1185
1186 else if (fpclassify(Real(z)) < FP_ZERO) { /* Real(z) = +INF or a NaN */
1187 Real(w) = cosval*Real(z); /* deserved invalid may occur */
1188 Imag(w) = sinval*Real(z); /* deserved invalid may occur */
1189 }
1190
1191 else if (Real(z) > 1460.0) { /* probable overflow case */
1192 Real(w) = scalbn(cosval,2100);
1193 Imag(w) = scalbn(sinval,2100);
1194 }
1195
1196 else { /* exp(Real(z)) overflows but product with sin or cos may not */
1197 Real(w) = cosval*FPKEXP709.d; /* initialize real result */
1198 Imag(w) = sinval*FPKEXP709.d; /* initialize imag result */
1199 exparg = Real(z) - 709.0; /* initialize reduced exponent argument */
1200 while (exparg > 709.0) { /* exponential reduction loop */
1201 Real(w) *= FPKEXP709.d;
1202 Imag(w) *= FPKEXP709.d;
1203 exparg -= 709.0;
1204 }
1205 expval = exp(exparg); /* final exponential value */
1206 Real(w) *= expval; /* final multiplication steps */
1207 Imag(w) *= expval;
1208 }
1209
1210 return w; /* done */
1211}
1212
1213float complex cexpf ( float complex z )
1214{
1215 float sinval, cosval, expval;
1216 float complex w;
1217
1218 sinval = sinf(Imag(z)); /* sine of imaginary part of argument */
1219 cosval = cosf(Imag(z)); /* cosine of imaginary part of argument */
1220
1221 if (Real(z) <= 88.0f) { /* exp(Real(z)) is finite */
1222 expval = expf(Real(z)); /* evaluate exponential */
1223 Real(w) = cosval*expval; /* real result = cos(Imag(z))*exp(Real(z)) */
1224 Imag(w) = sinval*expval; /* imag result = sin(Imag(z))*exp(Real(z)) */
1225 }
1226
1227 else // Handle edge cases in double
1228 w = (float complex)cexp((double complex)z);
1229
1230 return w; /* done */
1231}
1232
1233#if (__WANT_LONG_DOUBLE_FORMAT__ - 0L == 128L)
1234
1235// long double has same range as double
1236long double complex cexpl ( long double complex z )
1237{
1238 long double sinval, cosval, expval, exparg;
1239 long double complex w;
1240
1241 sinval = sinl(Imag(z)); /* sine of imaginary part of argument */
1242 cosval = cosl(Imag(z)); /* cosine of imaginary part of argument */
1243
1244 if (Real(z) <= 709.0L) { /* exp(Real(z)) is finite */
1245 expval = expl(Real(z)); /* evaluate exponential */
1246 Real(w) = cosval*expval; /* real result = cos(Imag(z))*exp(Real(z)) */
1247 Imag(w) = sinval*expval; /* imag result = sin(Imag(z))*exp(Real(z)) */
1248 }
1249
1250 else if (fpclassify(Real(z)) < FP_ZERO) { /* Real(z) = +INF or a NaN */
1251 Real(w) = cosval*Real(z); /* deserved invalid may occur */
1252 Imag(w) = sinval*Real(z); /* deserved invalid may occur */
1253 }
1254
1255 else if (Real(z) > 1460.0L) { /* probable overflow case */
1256 Real(w) = scalbnl(cosval,2100);
1257 Imag(w) = scalbnl(sinval,2100);
1258 }
1259
1260 else { /* exp(Real(z)) overflows but product with sin or cos may not */
1261 long double t = FPKEXP709L;
1262
1263 Real(w) = cosval*t; /* initialize real result */
1264 Imag(w) = sinval*t; /* initialize imag result */
1265 exparg = Real(z) - 709.0L; /* initialize reduced exponent argument */
1266 while (exparg > 709.0) { /* exponential reduction loop */
1267 Real(w) *= t;
1268 Imag(w) *= t;
1269 exparg -= 709.0L;
1270 }
1271 expval = expl(exparg); /* final exponential value */
1272 Real(w) *= expval; /* final multiplication steps */
1273 Imag(w) *= expval;
1274 }
1275
1276 return w; /* done */
1277}
1278#endif
1279
1280/****************************************************************************
1281 double complex cpow(double complex x, double complex y) returns the complex result of x^y.
1282 The algorithm is based upon the identity:
1283
1284 x^y = cexp(y*clog(x)).
1285
1286 Calls: clog, cexp.
1287****************************************************************************/
1288
1289double complex cpow ( double complex x, double complex y ) /* (complex x)^(complex y) */
1290{
1291 double complex logval,z;
1292
1293 logval = clog(x); /* complex logarithm of x */
1294 Real(z) = Real(y)*Real(logval) - Imag(y)*Imag(logval); /* multiply by y */
1295 Imag(z) = Real(y)*Imag(logval) + Imag(y)*Real(logval);
1296 return (cexp(z)); /* return complex exponential */
1297}
1298
1299float complex cpowf ( float complex x, float complex y ) /* (complex x)^(complex y) */
1300{
1301 float complex logval,z;
1302
1303 logval = clogf(x); /* complex logarithm of x */
1304 Real(z) = Real(y)*Real(logval) - Imag(y)*Imag(logval); /* multiply by y */
1305 Imag(z) = Real(y)*Imag(logval) + Imag(y)*Real(logval);
1306 return (cexpf(z)); /* return complex exponential */
1307}
1308
1309#if (__WANT_LONG_DOUBLE_FORMAT__ - 0L == 128L)
1310
1311long double complex cpowl ( long double complex x, long double complex y ) /* (complex x)^(complex y) */
1312{
1313 long double complex logval,z;
1314
1315 logval = clogl(x); /* complex logarithm of x */
1316 Real(z) = Real(y)*Real(logval) - Imag(y)*Imag(logval); /* multiply by y */
1317 Imag(z) = Real(y)*Imag(logval) + Imag(y)*Real(logval);
1318 return (cexpl(z)); /* return complex exponential */
1319}
1320#endif
1321
1322/****************************************************************************
1323 double complex ctanh(double complex x) returns the complex hyperbolic tangent of its
1324 argument. The algorithm is from Kahan's paper and is based on the
1325 identity:
1326
1327 tanh(x+i*y) = (sinh(2*x) + i*sin(2*y))/(cosh(2*x) + cos(2*y))
1328 = (cosh(x)*sinh(x)*cscsq + i*tan(y))/(1+cscsq*sinh(x)*sinh(x)),
1329
1330 where cscsq = 1/(cos(y)*cos(y). For large values of ze.re, spurious
1331 overflow and invalid signaling is avoided.
1332
1333 CONSTANTS: FPKASINHOM4 = asinh(nextafterd(+infinity,0.0))/4.0 to double
1334 precision
1335 FPKINF = +infinity
1336
1337 Calls: tan, sinh, sqrt, fabs, feholdexcept, feraiseexcept, feclearexcept,
1338 and feupdateenv.
1339****************************************************************************/
1340
1341double complex ctanh( double complex z )
1342{
1343 fenv_t env;
1344 double tanval, beta, sinhval, coshval, denom;
1345 double complex w;
1346
1347 (void)feholdexcept(&env); /* save environment and clear flags */
1348
1349 if (fabs(Real(z)) > FPKASINHOM4.d) { /* avoid overflow for large |Real(z)| */
1350 Real(w) = copysign(1.0,Real(z)); /* real result has unit magnitude */
1351 Imag(w) = copysign(0.0,Imag(z)); /* imag result is signed zero */
1352 if (fabs(Real(z)) != INFINITY) /* set inexact for finite Real(z) */
1353 feraiseexcept(FE_INEXACT);
1354 feupdateenv(&env); /* update environment */
1355 } /* end large |Real(z)| case */
1356
1357
1358 else { /* usual case */
1359 tanval = tan(Imag(z)); /* evaluate tangent */
1360 feclearexcept(FE_DIVBYZERO); /* in case tangent is infinite */
1361 feupdateenv(&env); /* update environment */
1362 beta = 1.0 + tanval*tanval; /* 1/(cos(Imag(z)))^2 */
1363 sinhval = sinh(Real(z)); /* evaluate sinh */
1364 coshval = sqrt(1.0+sinhval*sinhval); /* evaluate cosh */
1365
1366 if (fabs(tanval) == INFINITY) { /* infinite tangent */
1367 Real(w) = coshval/sinhval;
1368 Imag(w) = 1.0/tanval;
1369 }
1370
1371 else { /* finite tangent */
1372 denom = 1.0 + beta*sinhval*sinhval;
1373 Real(w) = beta*coshval*sinhval/denom;
1374 Imag(w) = tanval/denom;
1375 }
1376 } /* end usual case */
1377
1378 return w; /* done */
1379}
1380
1381float complex ctanhf( float complex z )
1382{
1383 fenv_t env;
1384 float tanval, beta, sinhval, coshval, denom;
1385 float complex w;
1386
1387 (void)feholdexcept(&env); /* save environment and clear flags */
1388
1389 if (fabsf(Real(z)) > FPKASINHOM4f.fval) { /* avoid overflow for large |Real(z)| */
1390 Real(w) = copysignf(1.0f,Real(z)); /* real result has unit magnitude */
1391 Imag(w) = copysignf(0.0f,Imag(z)); /* imag result is signed zero */
1392 if (fabsf(Real(z)) != INFINITY) /* set inexact for finite Real(z) */
1393 feraiseexcept(FE_INEXACT);
1394 feupdateenv(&env); /* update environment */
1395 } /* end large |Real(z)| case */
1396
1397
1398 else { /* usual case */
1399 tanval = tanf(Imag(z)); /* evaluate tangent */
1400 feclearexcept(FE_DIVBYZERO); /* in case tangent is infinite */
1401 feupdateenv(&env); /* update environment */
1402 beta = 1.0f + tanval*tanval; /* 1/(cos(Imag(z)))^2 */
1403 sinhval = sinhf(Real(z)); /* evaluate sinh */
1404 coshval = sqrtf(1.0f+sinhval*sinhval); /* evaluate cosh */
1405
1406 if (fabs(tanval) == INFINITY) { /* infinite tangent */
1407 Real(w) = coshval/sinhval;
1408 Imag(w) = 1.0f/tanval;
1409 }
1410
1411 else { /* finite tangent */
1412 denom = 1.0f + beta*sinhval*sinhval;
1413 Real(w) = beta*coshval*sinhval/denom;
1414 Imag(w) = tanval/denom;
1415 }
1416 } /* end usual case */
1417
1418 return w; /* done */
1419}
1420
1421#if (__WANT_LONG_DOUBLE_FORMAT__ - 0L == 128L)
1422
1423long double complex ctanhl( long double complex z )
1424{
1425 fenv_t env;
1426 long double tanval, beta, sinhval, coshval, denom;
1427 long double complex w;
1428
1429 (void)feholdexcept(&env); /* save environment and clear flags */
1430
1431 if (fabsl(Real(z)) > FPKASINHOM4.d) { /* avoid overflow for large |Real(z)| */
1432 Real(w) = copysignl(1.0L,Real(z)); /* real result has unit magnitude */
1433 Imag(w) = copysignl(0.0L,Imag(z)); /* imag result is signed zero */
1434 if (fabsl(Real(z)) != INFINITY) /* set inexact for finite Real(z) */
1435 feraiseexcept(FE_INEXACT);
1436 feupdateenv(&env); /* update environment */
1437 } /* end large |Real(z)| case */
1438
1439
1440 else { /* usual case */
1441 tanval = tanl(Imag(z)); /* evaluate tangent */
1442 feclearexcept(FE_DIVBYZERO); /* in case tangent is infinite */
1443 feupdateenv(&env); /* update environment */
1444 beta = 1.0L + tanval*tanval; /* 1/(cos(Imag(z)))^2 */
1445 sinhval = sinhl(Real(z)); /* evaluate sinh */
1446 coshval = sqrtl(1.0L+sinhval*sinhval); /* evaluate cosh */
1447
1448 if (fabsl(tanval) == INFINITY) { /* infinite tangent */
1449 Real(w) = coshval/sinhval;
1450 Imag(w) = 1.0L/tanval;
1451 }
1452
1453 else { /* finite tangent */
1454 denom = 1.0L + beta*sinhval*sinhval;
1455 Real(w) = beta*coshval*sinhval/denom;
1456 Imag(w) = tanval/denom;
1457 }
1458 } /* end usual case */
1459
1460 return w; /* done */
1461}
1462#endif
1463
1464/****************************************************************************
1465 double complex ctan(double complex x) returns the complex hyperbolic tangent of its
1466 argument. The algorithm is from Kahan's paper and is based on the
1467 identity:
1468
1469 tan(x + i*y) = (sin(2*x) + i*sinh(2*y))/(cos(2*x) + cosh(2*y))
1470 = (tan(x)+i*cosh(y)*sinh(y)*cscsq)/(1+cscsq*sinh(y)*sinh(y)),
1471
1472 where cscsq = 1/(cos(x)*cos(x). For large values of ze.im, spurious
1473 overflow and invalid signaling is avoided.
1474
1475 CONSTANTS: FPKASINHOM4 = asinh(nextafterd(+infinity,0.0))/4.0 to double
1476 precision
1477 FPKINF = +infinity
1478
1479 Calls: tan, sinh, sqrt, fabs, feholdexcept, feraiseexcept, feclearexcept,
1480 and feupdateenv.
1481****************************************************************************/
1482
1483double complex ctan( double complex z )
1484{
1485 fenv_t env;
1486 double tanval, beta, sinhval, coshval, denom;
1487 double complex w;
1488
1489 (void)feholdexcept(&env); /* save environment and clear flags */
1490
1491 if (fabs(Imag(z)) > FPKASINHOM4.d) { /* avoid overflow for large |Imag(z)| */
1492 Real(w) = copysign(0.0,Real(z)); /* real result has zero magnitude */
1493 Imag(w) = copysign(1.0,Imag(z)); /* imag result has unit magnitude */
1494 if (fabs(Imag(z)) != INFINITY) /* set inexact for finite Real(z) */
1495 feraiseexcept(FE_INEXACT);
1496 feupdateenv(&env); /* update environment */
1497 } /* end large |Real(z)| case */
1498
1499
1500 else { /* usual case */
1501 tanval = tan(Real(z)); /* evaluate tangent */
1502 feclearexcept(FE_DIVBYZERO); /* in case tangent is infinite */
1503 feupdateenv(&env); /* update environment */
1504 beta = 1.0 + tanval*tanval; /* 1/(cos(Real(z)))^2 */
1505 sinhval = sinh(Imag(z)); /* evaluate sinh */
1506 coshval = sqrt(1.0+sinhval*sinhval); /* evaluate cosh */
1507
1508 if (fabs(tanval) == INFINITY) { /* infinite tangent */
1509 Real(w) = 1.0/tanval;
1510 Imag(w) = coshval/sinhval;
1511 }
1512
1513 else { /* finite tangent */
1514 denom = 1.0 + beta*sinhval*sinhval;
1515 Real(w) = tanval/denom;
1516 Imag(w) = beta*coshval*sinhval/denom;
1517 }
1518 } /* end usual case */
1519
1520 return w; /* done */
1521}
1522
1523float complex ctanf( float complex z )
1524{
1525 fenv_t env;
1526 float tanval, beta, sinhval, coshval, denom;
1527 float complex w;
1528
1529 (void)feholdexcept(&env); /* save environment and clear flags */
1530
1531 if (fabsf(Imag(z)) > FPKASINHOM4f.fval) { /* avoid overflow for large |Imag(z)| */
1532 Real(w) = copysignf(0.0f,Real(z)); /* real result has zero magnitude */
1533 Imag(w) = copysignf(1.0f,Imag(z)); /* imag result has unit magnitude */
1534 if (fabsf(Imag(z)) != INFINITY) /* set inexact for finite Real(z) */
1535 feraiseexcept(FE_INEXACT);
1536 feupdateenv(&env); /* update environment */
1537 } /* end large |Real(z)| case */
1538
1539
1540 else { /* usual case */
1541 tanval = tanf(Real(z)); /* evaluate tangent */
1542 feclearexcept(FE_DIVBYZERO); /* in case tangent is infinite */
1543 feupdateenv(&env); /* update environment */
1544 beta = 1.0f + tanval*tanval; /* 1/(cos(Real(z)))^2 */
1545 sinhval = sinhf(Imag(z)); /* evaluate sinh */
1546 coshval = sqrtf(1.0f+sinhval*sinhval); /* evaluate cosh */
1547
1548 if (fabsf(tanval) == INFINITY) { /* infinite tangent */
1549 Real(w) = 1.0f/tanval;
1550 Imag(w) = coshval/sinhval;
1551 }
1552
1553 else { /* finite tangent */
1554 denom = 1.0f + beta*sinhval*sinhval;
1555 Real(w) = tanval/denom;
1556 Imag(w) = beta*coshval*sinhval/denom;
1557 }
1558 } /* end usual case */
1559
1560 return w; /* done */
1561}
1562
1563#if (__WANT_LONG_DOUBLE_FORMAT__ - 0L == 128L)
1564
1565long double complex ctanl( long double complex z )
1566{
1567 fenv_t env;
1568 long double tanval, beta, sinhval, coshval, denom;
1569 long double complex w;
1570
1571 (void)feholdexcept(&env); /* save environment and clear flags */
1572
1573 if (fabsl(Imag(z)) > FPKASINHOM4.d) { /* avoid overflow for large |Imag(z)| */
1574 Real(w) = copysignl(0.0L,Real(z)); /* real result has zero magnitude */
1575 Imag(w) = copysignl(1.0L,Imag(z)); /* imag result has unit magnitude */
1576 if (fabsl(Imag(z)) != INFINITY) /* set inexact for finite Real(z) */
1577 feraiseexcept(FE_INEXACT);
1578 feupdateenv(&env); /* update environment */
1579 } /* end large |Real(z)| case */
1580
1581
1582 else { /* usual case */
1583 tanval = tanl(Real(z)); /* evaluate tangent */
1584 feclearexcept(FE_DIVBYZERO); /* in case tangent is infinite */
1585 feupdateenv(&env); /* update environment */
1586 beta = 1.0L + tanval*tanval; /* 1/(cos(Real(z)))^2 */
1587 sinhval = sinhl(Imag(z)); /* evaluate sinh */
1588 coshval = sqrtl(1.0L+sinhval*sinhval); /* evaluate cosh */
1589
1590 if (fabsl(tanval) == INFINITY) { /* infinite tangent */
1591 Real(w) = 1.0L/tanval;
1592 Imag(w) = coshval/sinhval;
1593 }
1594
1595 else { /* finite tangent */
1596 denom = 1.0L + beta*sinhval*sinhval;
1597 Real(w) = tanval/denom;
1598 Imag(w) = beta*coshval*sinhval/denom;
1599 }
1600 } /* end usual case */
1601
1602 return w; /* done */
1603}
1604#endif
1605
1606/****************************************************************************
1607 double complex casin(double complex z) returns the complex inverse sine of its
1608 argument. The algorithm is from Kahan's paper and is based on the
1609 formulae:
1610
1611 real(casin(z)) = atan (real(z)/real(csqrt(1.0-z)*csqrt(1.0+z)))
1612 imag(casin(z)) = arcsinh(imag(csqrt(1.0-cconj(z))*csqrt(1.0+z)))
1613
1614 Calls: arcsinh, csqrt, atan, feholdexcept, feclearexcept, feupdateenv.
1615****************************************************************************/
1616
1617double complex casin ( double complex z )
1618{
1619 double complex zp1, zm, zm1, w;
1620 fenv_t env;
1621
1622 Real(zp1) = 1.0 + Real(z); /* evaluate zp1 = csqrt(1.0+z) */
1623 Imag(zp1) = Imag(z);
1624 zp1 = csqrt(zp1);
1625
1626 Real(zm) = 1.0 - Real(z); /* evaluate zm1 = csqrt(1.0-z) */
1627 Imag(zm) = -Imag(z);
1628 zm1 = csqrt(zm);
1629
1630 (void)feholdexcept(&env); /* save environ., clr flags/enables */
1631 Real(w) = atan(Real(z)/(Real(zp1)*Real(zm1) -Imag(zp1)*Imag(zm1))); /* real result */
1632 feclearexcept(FE_DIVBYZERO); /* clear spurious DIVBYZERO exception */
1633 feupdateenv(&env); /* restore environment with new flags */
1634
1635 Imag(zm) = Imag(z); /* evaluate zm1 = csqrt(1.0-cconj(z)) */
1636 zm1 = csqrt(zm);
1637
1638 Imag(w) = asinh(Real(zp1)*Imag(zm1) + Imag(zp1)*Real(zm1)); /* imag result */
1639
1640 return w;
1641}
1642
1643float complex casinf ( float complex z )
1644{
1645 float complex zp1, zm, zm1, w;
1646 fenv_t env;
1647
1648 Real(zp1) = 1.0f + Real(z); /* evaluate zp1 = csqrt(1.0+z) */
1649 Imag(zp1) = Imag(z);
1650 zp1 = csqrtf(zp1);
1651
1652 Real(zm) = 1.0f - Real(z); /* evaluate zm1 = csqrt(1.0-z) */
1653 Imag(zm) = -Imag(z);
1654 zm1 = csqrtf(zm);
1655
1656 (void)feholdexcept(&env); /* save environ., clr flags/enables */
1657 Real(w) = atanf(Real(z)/(Real(zp1)*Real(zm1) -Imag(zp1)*Imag(zm1))); /* real result */
1658 feclearexcept(FE_DIVBYZERO); /* clear spurious DIVBYZERO exception */
1659 feupdateenv(&env); /* restore environment with new flags */
1660
1661 Imag(zm) = Imag(z); /* evaluate zm1 = csqrt(1.0-cconj(z)) */
1662 zm1 = csqrtf(zm);
1663
1664 Imag(w) = asinhf(Real(zp1)*Imag(zm1) + Imag(zp1)*Real(zm1)); /* imag result */
1665
1666 return w;
1667}
1668
1669#if (__WANT_LONG_DOUBLE_FORMAT__ - 0L == 128L)
1670
1671long double complex casinl ( long double complex z )
1672{
1673 long double complex zp1, zm, zm1, w;
1674 fenv_t env;
1675
1676 Real(zp1) = 1.0L + Real(z); /* evaluate zp1 = csqrt(1.0+z) */
1677 Imag(zp1) = Imag(z);
1678 zp1 = csqrtl(zp1);
1679
1680 Real(zm) = 1.0L - Real(z); /* evaluate zm1 = csqrt(1.0-z) */
1681 Imag(zm) = -Imag(z);
1682 zm1 = csqrtl(zm);
1683
1684 (void)feholdexcept(&env); /* save environ., clr flags/enables */
1685 Real(w) = atanl(Real(z)/(Real(zp1)*Real(zm1) -Imag(zp1)*Imag(zm1))); /* real result */
1686 feclearexcept(FE_DIVBYZERO); /* clear spurious DIVBYZERO exception */
1687 feupdateenv(&env); /* restore environment with new flags */
1688
1689 Imag(zm) = Imag(z); /* evaluate zm1 = csqrt(1.0-cconj(z)) */
1690 zm1 = csqrtl(zm);
1691
1692 Imag(w) = asinhl(Real(zp1)*Imag(zm1) + Imag(zp1)*Real(zm1)); /* imag result */
1693
1694 return w;
1695}
1696#endif
1697
1698/****************************************************************************
1699 double complex casinh(double complex z) returns the complex inverse hyperbolic sine of
1700 its argument. The algorithm is from Kahan's paper and is based on the
1701 formula:
1702
1703 casinh(z) = -i*casin(i*z).
1704
1705 Calls: arcsinh, csqrt, atan, feholdexcept, feclearexcept, feupdateenv.
1706****************************************************************************/
1707
1708double complex casinh ( double complex z )
1709{
1710 double complex zp1, zm, zm1, w;
1711 fenv_t env;
1712
1713 Real(zp1) = 1.0 - Imag(z); /* evaluate zp1 = csqrt(1.0+i*z) */
1714 Imag(zp1) = Real(z);
1715 zp1 = csqrt(zp1);
1716
1717 Real(zm) = 1.0 + Imag(z); /* evaluate zm1 = csqrt(1.0-cconj(i*z)) */
1718 Imag(zm) = Real(z);
1719 zm1 = csqrt(zm);
1720
1721 Real(w) = asinh(Real(zp1)*Imag(zm1) + Imag(zp1)*Real(zm1)); /* imag result */
1722
1723 Imag(zm) = -Real(z); /* evaluate zm1 = csqrt(1.0-cconj(z)) */
1724 zm1 = csqrt(zm);
1725
1726 (void)feholdexcept(&env); /* save environ., clr flags/enables */
1727 Imag(w) = atan(Imag(z)/(Real(zp1)*Real(zm1) -Imag(zp1)*Imag(zm1))); /* real result */
1728 feclearexcept(FE_DIVBYZERO); /* clear spurious DIVBYZERO exception */
1729 feupdateenv(&env); /* restore environment with new flags */
1730
1731 return w;
1732}
1733
1734float complex casinhf ( float complex z )
1735{
1736 float complex zp1, zm, zm1, w;
1737 fenv_t env;
1738
1739 Real(zp1) = 1.0f - Imag(z); /* evaluate zp1 = csqrt(1.0+i*z) */
1740 Imag(zp1) = Real(z);
1741 zp1 = csqrt(zp1);
1742
1743 Real(zm) = 1.0f + Imag(z); /* evaluate zm1 = csqrt(1.0-cconj(i*z)) */
1744 Imag(zm) = Real(z);
1745 zm1 = csqrtf(zm);
1746
1747 Real(w) = asinhf(Real(zp1)*Imag(zm1) + Imag(zp1)*Real(zm1)); /* imag result */
1748
1749 Imag(zm) = -Real(z); /* evaluate zm1 = csqrt(1.0-cconj(z)) */
1750 zm1 = csqrtf(zm);
1751
1752 (void)feholdexcept(&env); /* save environ., clr flags/enables */
1753 Imag(w) = atanf(Imag(z)/(Real(zp1)*Real(zm1) -Imag(zp1)*Imag(zm1))); /* real result */
1754 feclearexcept(FE_DIVBYZERO); /* clear spurious DIVBYZERO exception */
1755 feupdateenv(&env); /* restore environment with new flags */
1756
1757 return w;
1758}
1759
1760#if (__WANT_LONG_DOUBLE_FORMAT__ - 0L == 128L)
1761
1762long double complex casinhl ( long double complex z )
1763{
1764 long double complex zp1, zm, zm1, w;
1765 fenv_t env;
1766
1767 Real(zp1) = 1.0L - Imag(z); /* evaluate zp1 = csqrt(1.0+i*z) */
1768 Imag(zp1) = Real(z);
1769 zp1 = csqrtl(zp1);
1770
1771 Real(zm) = 1.0L + Imag(z); /* evaluate zm1 = csqrt(1.0-cconj(i*z)) */
1772 Imag(zm) = Real(z);
1773 zm1 = csqrtl(zm);
1774
1775 Real(w) = asinhl(Real(zp1)*Imag(zm1) + Imag(zp1)*Real(zm1)); /* imag result */
1776
1777 Imag(zm) = -Real(z); /* evaluate zm1 = csqrt(1.0-cconj(z)) */
1778 zm1 = csqrtl(zm);
1779
1780 (void)feholdexcept(&env); /* save environ., clr flags/enables */
1781 Imag(w) = atanl(Imag(z)/(Real(zp1)*Real(zm1) -Imag(zp1)*Imag(zm1))); /* real result */
1782 feclearexcept(FE_DIVBYZERO); /* clear spurious DIVBYZERO exception */
1783 feupdateenv(&env); /* restore environment with new flags */
1784
1785 return w;
1786}
1787#endif
1788
1789/****************************************************************************
1790 double complex cacos(double complex z) returns the complex inverse cosine of its
1791 argument. The algorithm is from Kahan's paper and is based on the
1792 formulae:
1793
1794 real(cacos(z)) = 2.0*atan(real(csqrt(1.0-z)/real(csqrt(1.0+z))))
1795 imag(cacos(z)) = arcsinh(imag(csqrt(1.0-z)*csqrt(cconj(1.0+z))))
1796
1797 Calls: arcsinh, csqrt, atan, feholdexcept, feclearexcept, feupdateenv.
1798****************************************************************************/
1799
1800double complex cacos ( double complex z )
1801{
1802 double complex zp, zp1, zm1, w;
1803 fenv_t env;
1804
1805 Real(zp) = 1.0 + Real(z); /* zp1 = csqrt(1.0 + z) */
1806 Imag(zp) = Imag(z);
1807 zp1 = csqrt(zp);
1808
1809 Real(zm1) = 1.0 - Real(z); /* zm1 = csqrt(1.0 - z) */
1810 Imag(zm1) = -Imag(z);
1811 zm1 = csqrt(zm1);
1812
1813 (void)feholdexcept(&env); /* save environment, clr flags/enables */
1814 Real(w) = 2.0*atan(Real(zm1)/Real(zp1)); /* real result */
1815 feclearexcept(FE_DIVBYZERO); /* clr possible spurious div-by-zero flag */
1816 feupdateenv(&env); /* update environment with new flags */
1817
1818 Imag(zp) = -Imag(z); /* zp1 = csqrt(1.0 + cconj(z)) */
1819 zp1 = csqrt(zp);
1820
1821 Imag(w) = asinh(Real(zp1)*Imag(zm1) + Imag(zp1)*Real(zm1)); /* imag result */
1822
1823 return w;
1824}
1825
1826float complex cacosf ( float complex z )
1827{
1828 float complex zp, zp1, zm1, w;
1829 fenv_t env;
1830
1831 Real(zp) = 1.0f + Real(z); /* zp1 = csqrt(1.0 + z) */
1832 Imag(zp) = Imag(z);
1833 zp1 = csqrtf(zp);
1834
1835 Real(zm1) = 1.0f - Real(z); /* zm1 = csqrt(1.0 - z) */
1836 Imag(zm1) = -Imag(z);
1837 zm1 = csqrtf(zm1);
1838
1839 (void)feholdexcept(&env); /* save environment, clr flags/enables */
1840 Real(w) = 2.0f*atanf(Real(zm1)/Real(zp1)); /* real result */
1841 feclearexcept(FE_DIVBYZERO); /* clr possible spurious div-by-zero flag */
1842 feupdateenv(&env); /* update environment with new flags */
1843
1844 Imag(zp) = -Imag(z); /* zp1 = csqrt(1.0 + cconj(z)) */
1845 zp1 = csqrtf(zp);
1846
1847 Imag(w) = asinhf(Real(zp1)*Imag(zm1) + Imag(zp1)*Real(zm1)); /* imag result */
1848
1849 return w;
1850}
1851
1852#if (__WANT_LONG_DOUBLE_FORMAT__ - 0L == 128L)
1853
1854long double complex cacosl ( long double complex z )
1855{
1856 long double complex zp, zp1, zm1, w;
1857 fenv_t env;
1858
1859 Real(zp) = 1.0L + Real(z); /* zp1 = csqrt(1.0 + z) */
1860 Imag(zp) = Imag(z);
1861 zp1 = csqrtl(zp);
1862
1863 Real(zm1) = 1.0L - Real(z); /* zm1 = csqrt(1.0 - z) */
1864 Imag(zm1) = -Imag(z);
1865 zm1 = csqrtl(zm1);
1866
1867 (void)feholdexcept(&env); /* save environment, clr flags/enables */
1868 Real(w) = 2.0L*atanl(Real(zm1)/Real(zp1)); /* real result */
1869 feclearexcept(FE_DIVBYZERO); /* clr possible spurious div-by-zero flag */
1870 feupdateenv(&env); /* update environment with new flags */
1871
1872 Imag(zp) = -Imag(z); /* zp1 = csqrt(1.0 + cconj(z)) */
1873 zp1 = csqrtl(zp);
1874
1875 Imag(w) = asinhl(Real(zp1)*Imag(zm1) + Imag(zp1)*Real(zm1)); /* imag result */
1876
1877 return w;
1878}
1879#endif
1880
1881
1882/****************************************************************************
1883 double complex cacosh(double complex z) returns the complex inverse hyperbolic`cosine
1884 of its argument. The algorithm is from Kahan's paper and is based on the
1885 formulae:
1886
1887 real(cacosh(z)) = arcsinh(real(csqrt(cconj(z)-1.0)*csqrt(z+1.0)))
1888 imag(cacosh(z)) = 2.0*atan(imag(csqrt(z-1.0)/real(csqrt(z+1.0))))
1889
1890 Calls: arcsinh, csqrt, atan, feholdexcept, feclearexcept, feupdateenv.
1891****************************************************************************/
1892
1893double complex cacosh ( double complex z )
1894{
1895 double complex zp1, zm, zm1, w;
1896 fenv_t env;
1897
1898 Real(zp1) = Real(z) + 1.0; /* zp1 = csqrt(z + 1.0) */
1899 Imag(zp1) = Imag(z);
1900 zp1 = csqrt(zp1);
1901
1902 Real(zm) = Real(z) - 1.0; /* zm1 = csqrt(z - 1.0) */
1903 Imag(zm) = Imag(z);
1904 zm1 = csqrt(zm);
1905
1906 (void)feholdexcept(&env); /* save environment, clr flags/enables */
1907 Imag(w) = 2.0*atan(Imag(zm1)/Real(zp1)); /* imag result */
1908 feclearexcept(FE_DIVBYZERO); /* clr possible spurious div-by-zero flag */
1909 feupdateenv(&env); /* restore environment with new flags */
1910
1911 Imag(zm) = -Imag(z); /* zm1 = csqrt(cconj(z) - 1.0) */
1912 zm1 = csqrt(zm);
1913
1914 Real(w) = asinh(Real(zp1)*Real(zm1) - Imag(zp1)*Imag(zm1)); /* real result */
1915
1916 return w;
1917}
1918
1919float complex cacoshf ( float complex z )
1920{
1921 float complex zp1, zm, zm1, w;
1922 fenv_t env;
1923
1924 Real(zp1) = Real(z) + 1.0f; /* zp1 = csqrt(z + 1.0) */
1925 Imag(zp1) = Imag(z);
1926 zp1 = csqrtf(zp1);
1927
1928 Real(zm) = Real(z) - 1.0f; /* zm1 = csqrt(z - 1.0) */
1929 Imag(zm) = Imag(z);
1930 zm1 = csqrtf(zm);
1931
1932 (void)feholdexcept(&env); /* save environment, clr flags/enables */
1933 Imag(w) = 2.0f*atanf(Imag(zm1)/Real(zp1)); /* imag result */
1934 feclearexcept(FE_DIVBYZERO); /* clr possible spurious div-by-zero flag */
1935 feupdateenv(&env); /* restore environment with new flags */
1936
1937 Imag(zm) = -Imag(z); /* zm1 = csqrt(cconj(z) - 1.0) */
1938 zm1 = csqrtf(zm);
1939
1940 Real(w) = asinhf(Real(zp1)*Real(zm1) - Imag(zp1)*Imag(zm1)); /* real result */
1941
1942 return w;
1943}
1944
1945#if (__WANT_LONG_DOUBLE_FORMAT__ - 0L == 128L)
1946
1947long double complex cacoshl ( long double complex z )
1948{
1949 long double complex zp1, zm, zm1, w;
1950 fenv_t env;
1951
1952 Real(zp1) = Real(z) + 1.0L; /* zp1 = csqrt(z + 1.0) */
1953 Imag(zp1) = Imag(z);
1954 zp1 = csqrtl(zp1);
1955
1956 Real(zm) = Real(z) - 1.0L; /* zm1 = csqrt(z - 1.0) */
1957 Imag(zm) = Imag(z);
1958 zm1 = csqrtl(zm);
1959
1960 (void)feholdexcept(&env); /* save environment, clr flags/enables */
1961 Imag(w) = 2.0L*atanl(Imag(zm1)/Real(zp1)); /* imag result */
1962 feclearexcept(FE_DIVBYZERO); /* clr possible spurious div-by-zero flag */
1963 feupdateenv(&env); /* restore environment with new flags */
1964
1965 Imag(zm) = -Imag(z); /* zm1 = csqrt(cconj(z) - 1.0) */
1966 zm1 = csqrtl(zm);
1967
1968 Real(w) = asinhl(Real(zp1)*Real(zm1) - Imag(zp1)*Imag(zm1)); /* real result */
1969
1970 return w;
1971}
1972#endif
1973
1974/****************************************************************************
1975 double complex catan(double complex z) returns the complex inverse tangent
1976 of its argument. The algorithm is from Kahan's paper and is based on
1977 the formula:
1978
1979 catan(z) = i*(clog(1.0-i*z) - clog(1+i*z))/2.0.
1980
1981 CONSTANTS: FPKTHETA = sqrt(nextafterd(+INF,0.0))/4.0
1982 FPKRHO = 1.0/FPKTHETA
1983 FPKPI2 = pi/2.0
1984 FPKPI = pi
1985
1986 Calls: copysign, fabs, xdivc, sqrt, log, atan, log1p, and carg.
1987****************************************************************************/
1988
1989double complex catan ( double complex z )
1990{
1991 double complex ctemp, w;
1992 double t1, t2, xi, eta, beta;
1993
1994 xi = -Imag(z);
1995 beta = copysign(1.0,xi); /* copes with unsigned zero */
1996
1997 Imag(z) = -beta*Real(z); /* transform real & imag components */
1998 Real(z) = beta*xi;
1999
2000 if ((Real(z) > FPKTHETA.d) || (fabs(Imag(z)) > FPKTHETA.d)) {
2001 xi = copysign(M_PI_2,Imag(z)); /* avoid spurious overflow */
2002 ctemp = xdivc(1.0,z);
2003 eta = Real(ctemp);
2004 }
2005
2006 else if (Real(z) == 1.0) {
2007 t1 = fabs(Imag(z)) + FPKRHO.d;
2008 xi = log(sqrt(sqrt(4.0 + t1*t1))/sqrt(fabs(Imag(z))));
2009 eta = 0.5*copysign(M_PI-atan(2.0/(fabs(Imag(z))+FPKRHO.d)),Imag(z));
2010 }
2011
2012 else { /* usual case */
2013 t2 = fabs(Imag(z)) + FPKRHO.d;
2014 t1 = 1.0 - Real(z);
2015 t2 = t2*t2;
2016 xi = 0.25*log1p(4.0*Real(z)/(t1*t1 + t2));
2017 Real(ctemp) = (1.0 - Real(z))*(1.0 + Real(z)) - t2;
2018 Imag(ctemp) = Imag(z) + Imag(z);
2019 eta = 0.5*carg(ctemp);
2020 }
2021
2022 Real(w) = -beta*eta; /* fix up signs of result */
2023 Imag(w) = -beta*xi;
2024 return w;
2025}
2026
2027float complex catanf ( float complex z )
2028{
2029 float complex ctemp, w;
2030 float t1, t2, xi, eta, beta;
2031
2032 xi = -Imag(z);
2033 beta = copysignf(1.0f,xi); /* copes with unsigned zero */
2034
2035 Imag(z) = -beta*Real(z); /* transform real & imag components */
2036 Real(z) = beta*xi;
2037
2038 if ((Real(z) > FPKTHETAf.fval) || (fabsf(Imag(z)) > FPKTHETAf.fval)) {
2039 xi = copysignf((float) M_PI_2,Imag(z)); /* avoid spurious overflow */
2040 ctemp = xdivcf(1.0f,z);
2041 eta = Real(ctemp);
2042 }
2043
2044 else if (Real(z) == 1.0f) {
2045 t1 = fabsf(Imag(z)) + FPKRHOf.fval;
2046 xi = logf(sqrtf(sqrtf(4.0f + t1*t1))/sqrtf(fabsf(Imag(z))));
2047 eta = 0.5f*copysignf((float)( M_PI-atan(2.0/(fabsf(Imag(z))+FPKRHOf.fval))),Imag(z));
2048 }
2049
2050 else { /* usual case */
2051 t2 = fabsf(Imag(z)) + FPKRHOf.fval;
2052 t1 = 1.0f - Real(z);
2053 t2 = t2*t2;
2054 xi = 0.25f*log1pf(4.0f*Real(z)/(t1*t1 + t2));
2055 Real(ctemp) = (1.0f - Real(z))*(1.0f + Real(z)) - t2;
2056 Imag(ctemp) = Imag(z) + Imag(z);
2057 eta = 0.5f*cargf(ctemp);
2058 }
2059
2060 Real(w) = -beta*eta; /* fix up signs of result */
2061 Imag(w) = -beta*xi;
2062 return w;
2063}
2064
2065#if (__WANT_LONG_DOUBLE_FORMAT__ - 0L == 128L)
2066static const
2067 hexdbldbl FPKRHOl = {{0x20100000, 0x00000001, 0x9cbfffff, 0xffffffff}};
2068
2069long double complex catanl ( long double complex z )
2070{
2071 long double complex ctemp, w;
2072 long double t1, t2, xi, eta, beta;
2073
2074 xi = -Imag(z);
2075 beta = copysignl(1.0L,xi); /* copes with unsigned zero */
2076
2077 Imag(z) = -beta*Real(z); /* transform real & imag components */
2078 Real(z) = beta*xi;
2079
2080 if ((Real(z) > FPKTHETA.d) || (fabsl(Imag(z)) > FPKTHETA.d)) {
2081 xi = copysignl(M_PI_2,Imag(z)); /* avoid spurious overflow */
2082 ctemp = xdivcl(1.0L,z);
2083 eta = Real(ctemp);
2084 }
2085
2086 else if (Real(z) == 1.0L) {
2087 t1 = fabsl(Imag(z)) + FPKRHOl.ld;
2088 xi = logl(sqrtl(sqrtl(4.0L + t1*t1))/sqrtl(fabsl(Imag(z))));
2089 eta = 0.5L*copysignl(M_PI-atanl(2.0L/(fabsl(Imag(z))+FPKRHOl.ld)),Imag(z));
2090 }
2091
2092 else { /* usual case */
2093 t2 = fabsl(Imag(z)) + FPKRHOl.ld;
2094 t1 = 1.0L - Real(z);
2095 t2 = t2*t2;
2096 xi = 0.25L*log1pl(4.0L*Real(z)/(t1*t1 + t2));
2097 Real(ctemp) = (1.0L - Real(z))*(1.0L + Real(z)) - t2;
2098 Imag(ctemp) = Imag(z) + Imag(z);
2099 eta = 0.5L*cargl(ctemp);
2100 }
2101
2102 Real(w) = -beta*eta; /* fix up signs of result */
2103 Imag(w) = -beta*xi;
2104 return w;
2105}
2106#endif
2107
2108/****************************************************************************
2109 double complex catanh(double complex z) returns the complex inverse hyperbolic tangent
2110 of its argument. The algorithm is from Kahan's paper and is based on
2111 the formula:
2112
2113 catanh(z) = (clog(1.0 + z) - clog(1 - z))/2.0.
2114
2115 CONSTANTS: FPKTHETA = sqrt(nextafterd(+INF,0.0))/4.0
2116 FPKRHO = 1.0/FPKTHETA
2117 FPKPI2 = pi/2.0
2118 FPKPI = pi
2119
2120 Calls: copysign, fabs, xdivc, sqrt, log, atan, log1p, and carg.
2121****************************************************************************/
2122
2123double complex catanh( double complex z )
2124{
2125 double complex ctemp, w;
2126 double t1, t2, xi, eta, beta;
2127
2128 beta = copysign(1.0,Real(z)); /* copes with unsigned zero */
2129
2130 Imag(z) = -beta*Imag(z); /* transform real & imag components */
2131 Real(z) = beta*Real(z);
2132
2133 if ((Real(z) > FPKTHETA.d) || (fabs(Imag(z)) > FPKTHETA.d)) {
2134 eta = copysign(M_PI_2,Imag(z)); /* avoid overflow */
2135 ctemp = xdivc(1.0,z);
2136 xi = Real(ctemp);
2137 }
2138
2139 else if (Real(z) == 1.0) {
2140 t1 = fabs(Imag(z)) + FPKRHO.d;
2141 xi = log(sqrt(sqrt(4.0 + t1*t1))/sqrt(fabs(Imag(z))));
2142 eta = 0.5*copysign(M_PI-atan(2.0/(fabs(Imag(z))+FPKRHO.d)),Imag(z));
2143 }
2144
2145 else { /* usual case */
2146 t2 = fabs(Imag(z)) + FPKRHO.d;
2147 t1 = 1.0 - Real(z);
2148 t2 = t2*t2;
2149 xi = 0.25*log1p(4.0*Real(z)/(t1*t1 + t2));
2150 Real(ctemp) = (1.0 - Real(z))*(1.0 + Real(z)) - t2;
2151 Imag(ctemp) = Imag(z) + Imag(z);
2152 eta = 0.5*carg(ctemp);
2153 }
2154
2155 Real(w) = beta*xi; /* fix up signs of result */
2156 Imag(w) = -beta*eta;
2157 return w;
2158}
2159
2160float complex catanhf( float complex z )
2161{
2162 float complex ctemp, w;
2163 float t1, t2, xi, eta, beta;
2164
2165 beta = copysignf(1.0f,Real(z)); /* copes with unsigned zero */
2166
2167 Imag(z) = -beta*Imag(z); /* transform real & imag components */
2168 Real(z) = beta*Real(z);
2169
2170 if ((Real(z) > FPKTHETAf.fval) || (fabsf(Imag(z)) > FPKTHETAf.fval)) {
2171 eta = copysignf((float) M_PI_2,Imag(z)); /* avoid overflow */
2172 ctemp = xdivcf(1.0f,z);
2173 xi = Real(ctemp);
2174 }
2175
2176 else if (Real(z) == 1.0f) {
2177 t1 = fabsf(Imag(z)) + FPKRHOf.fval;
2178 xi = logf(sqrtf(sqrtf(4.0f + t1*t1))/sqrtf(fabsf(Imag(z))));
2179 eta = 0.5f*copysignf((float)( M_PI-atan(2.0f/(fabsf(Imag(z))+FPKRHOf.fval))),Imag(z));
2180 }
2181
2182 else { /* usual case */
2183 t2 = fabsf(Imag(z)) + FPKRHOf.fval;
2184 t1 = 1.0f - Real(z);
2185 t2 = t2*t2;
2186 xi = 0.25f*log1pf(4.0f*Real(z)/(t1*t1 + t2));
2187 Real(ctemp) = (1.0f - Real(z))*(1.0f + Real(z)) - t2;
2188 Imag(ctemp) = Imag(z) + Imag(z);
2189 eta = 0.5f*cargf(ctemp);
2190 }
2191
2192 Real(w) = beta*xi; /* fix up signs of result */
2193 Imag(w) = -beta*eta;
2194 return w;
2195}
2196
2197#if (__WANT_LONG_DOUBLE_FORMAT__ - 0L == 128L)
2198long double complex catanhl( long double complex z )
2199{
2200 long double complex ctemp, w;
2201 long double t1, t2, xi, eta, beta;
2202
2203 beta = copysignl(1.0L,Real(z)); /* copes with unsigned zero */
2204
2205 Imag(z) = -beta*Imag(z); /* transform real & imag components */
2206 Real(z) = beta*Real(z);
2207
2208 if ((Real(z) > FPKTHETA.d) || (fabsl(Imag(z)) > FPKTHETA.d)) {
2209 eta = copysignl(M_PI_2,Imag(z)); /* avoid overflow */
2210 ctemp = xdivcl(1.0L,z);
2211 xi = Real(ctemp);
2212 }
2213
2214 else if (Real(z) == 1.0L) {
2215 t1 = fabsl(Imag(z)) + FPKRHOl.ld;
2216 xi = logl(sqrtl(sqrtl(4.0L + t1*t1))/sqrtl(fabsl(Imag(z))));
2217 eta = 0.5L*copysignl(M_PI-atanl(2.0L/(fabsl(Imag(z))+FPKRHOl.ld)),Imag(z));
2218 }
2219
2220 else { /* usual case */
2221 t2 = fabsl(Imag(z)) + FPKRHOl.ld;
2222 t1 = 1.0L - Real(z);
2223 t2 = t2*t2;
2224 xi = 0.25L*log1pl(4.0L*Real(z)/(t1*t1 + t2));
2225 Real(ctemp) = (1.0L - Real(z))*(1.0L + Real(z)) - t2;
2226 Imag(ctemp) = Imag(z) + Imag(z);
2227 eta = 0.5L*cargl(ctemp);
2228 }
2229
2230 Real(w) = beta*xi; /* fix up signs of result */
2231 Imag(w) = -beta*eta;
2232 return w;
2233}
2234#endif
2235
2236/* conj(), creal(), and cimag() are gcc built ins. */
2237double creal( double complex z )
2238{
2239 return __builtin_creal(z);
2240}
2241
2242float crealf( float complex z )
2243{
2244 return __builtin_crealf(z);
2245}
2246
2247#if (__WANT_LONG_DOUBLE_FORMAT__ - 0L == 128L)
2248
2249long double creall( long double complex z )
2250{
2251 return __builtin_creall(z);
2252}
2253#endif
2254
2255double cimag( double complex z )
2256{
2257 return __builtin_cimag(z);
2258}
2259
2260float cimagf( float complex z )
2261{
2262 return __builtin_cimagf(z);
2263}
2264
2265#if (__WANT_LONG_DOUBLE_FORMAT__ - 0L == 128L)
2266
2267long double cimagl( long double complex z )
2268{
2269 return __builtin_cimagl(z);
2270}
2271#endif
2272
2273double complex conj( double complex z )
2274{
2275 return __builtin_conj(z);
2276}
2277
2278float complex conjf( float complex z )
2279{
2280 return __builtin_conjf(z);
2281}
2282
2283#if (__WANT_LONG_DOUBLE_FORMAT__ - 0L == 128L)
2284
2285long double complex conjl( long double complex z )
2286{
2287 return __builtin_conjl(z);
2288}
2289#endif
2290
2291double complex cproj( double complex z )
2292{
2293 static const double inf = __builtin_inf();
2294 double u = __builtin_fabs(Real(z));
2295 double v = __builtin_fabs(Imag(z));
2296
2297 if (unlikely((u == inf) || (v == inf)))
2298 return inf + I*copysign(0.0, Imag(z));
2299 else
2300 return z;
2301}
2302
2303float complex cprojf( float complex z )
2304{
2305 static const double inf = __builtin_inff();
2306 float u = __builtin_fabsf(Real(z));
2307 float v = __builtin_fabsf(Imag(z));
2308
2309 if (unlikely((u == inf) || (v == inf)))
2310 return inf + I*copysignf(0.0f, Imag(z));
2311 else
2312 return z;
2313}
2314
2315#if (__WANT_LONG_DOUBLE_FORMAT__ - 0L == 128L)
2316// PPC long double
2317long double complex cprojl( long double complex z )
2318{
2319 static const double inf = __builtin_infl();
2320 float u = __builtin_fabsl(Real(z));
2321 float v = __builtin_fabsl(Imag(z));
2322
2323 if (unlikely((u == inf) || (v == inf)))
2324 return inf + I*copysignl(0.0l, Imag(z));
2325 else
2326 return z;
2327}
2328#endif