1 // Written in the D programming language.
2
3 /**
4 This is a submodule of $(MREF std, math).
5
6 It contains several trigonometric functions.
7
8 Copyright: Copyright The D Language Foundation 2000 - 2011.
9 D implementations of tan, atan, and atan2 functions are based on the
10 CEPHES math library, which is Copyright (C) 2001 Stephen L. Moshier
11 $(LT)steve@moshier.net$(GT) and are incorporated herein by permission
12 of the author. The author reserves the right to distribute this
13 material elsewhere under different copying permissions.
14 These modifications are distributed here under the following terms:
15 License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
16 Authors: $(HTTP digitalmars.com, Walter Bright), Don Clugston,
17 Conversion of CEPHES math library to D by Iain Buclaw and David Nadlinger
18 Source: $(PHOBOSSRC std/math/trigonometry.d)
19
20 Macros:
21 TABLE_SV = <table border="1" cellpadding="4" cellspacing="0">
22 <caption>Special Values</caption>
23 $0</table>
24 SVH = $(TR $(TH $1) $(TH $2))
25 SV = $(TR $(TD $1) $(TD $2))
26 TH3 = $(TR $(TH $1) $(TH $2) $(TH $3))
27 TD3 = $(TR $(TD $1) $(TD $2) $(TD $3))
28 TABLE_DOMRG = <table border="1" cellpadding="4" cellspacing="0">
29 $(SVH Domain X, Range Y)
30 $(SV $1, $2)
31 </table>
32 DOMAIN=$1
33 RANGE=$1
34 POWER = $1<sup>$2</sup>
35 NAN = $(RED NAN)
36 PLUSMN = ±
37 INFIN = ∞
38 PLUSMNINF = ±∞
39 */
40
41 module std.math.trigonometry;
42
43 static import core.math;
44
45 version (D_InlineAsm_X86) version = InlineAsm_X86_Any;
46 version (D_InlineAsm_X86_64) version = InlineAsm_X86_Any;
47
48 version (InlineAsm_X86_Any) version = InlineAsm_X87;
version(InlineAsm_X87)49 version (InlineAsm_X87)
50 {
51 static assert(real.mant_dig == 64);
52 version (CRuntime_Microsoft) version = InlineAsm_X87_MSVC;
53 }
54
55 /***********************************
56 * Returns cosine of x. x is in radians.
57 *
58 * $(TABLE_SV
59 * $(TR $(TH x) $(TH cos(x)) $(TH invalid?))
60 * $(TR $(TD $(NAN)) $(TD $(NAN)) $(TD yes) )
61 * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD $(NAN)) $(TD yes) )
62 * )
63 * Bugs:
64 * Results are undefined if |x| >= $(POWER 2,64).
65 */
pragma(inline,true)66 pragma(inline, true)
67 real cos(real x) @safe pure nothrow @nogc { return core.math.cos(x); }
68 ///ditto
pragma(inline,true)69 pragma(inline, true)
70 double cos(double x) @safe pure nothrow @nogc { return core.math.cos(x); }
71 ///ditto
pragma(inline,true)72 pragma(inline, true)
73 float cos(float x) @safe pure nothrow @nogc { return core.math.cos(x); }
74
75 ///
76 @safe unittest
77 {
78 import std.math.operations : isClose;
79
80 assert(cos(0.0) == 1.0);
81 assert(cos(1.0).isClose(0.5403023059));
82 assert(cos(3.0).isClose(-0.9899924966));
83 }
84
85 @safe unittest
86 {
87 real function(real) pcos = &cos;
88 assert(pcos != null);
89 }
90
91 @safe pure nothrow @nogc unittest
92 {
93 import std.math.algebraic : fabs;
94
95 float f = cos(-2.0f);
96 assert(fabs(f - -0.416147f) < .00001);
97
98 double d = cos(-2.0);
99 assert(fabs(d - -0.416147f) < .00001);
100
101 real r = cos(-2.0L);
102 assert(fabs(r - -0.416147f) < .00001);
103 }
104
105 /***********************************
106 * Returns $(HTTP en.wikipedia.org/wiki/Sine, sine) of x. x is in $(HTTP en.wikipedia.org/wiki/Radian, radians).
107 *
108 * $(TABLE_SV
109 * $(TH3 x , sin(x) , invalid?)
110 * $(TD3 $(NAN) , $(NAN) , yes )
111 * $(TD3 $(PLUSMN)0.0, $(PLUSMN)0.0, no )
112 * $(TD3 $(PLUSMNINF), $(NAN) , yes )
113 * )
114 *
115 * Params:
116 * x = angle in radians (not degrees)
117 * Returns:
118 * sine of x
119 * See_Also:
120 * $(MYREF cos), $(MYREF tan), $(MYREF asin)
121 * Bugs:
122 * Results are undefined if |x| >= $(POWER 2,64).
123 */
pragma(inline,true)124 pragma(inline, true)
125 real sin(real x) @safe pure nothrow @nogc { return core.math.sin(x); }
126 ///ditto
pragma(inline,true)127 pragma(inline, true)
128 double sin(double x) @safe pure nothrow @nogc { return core.math.sin(x); }
129 ///ditto
pragma(inline,true)130 pragma(inline, true)
131 float sin(float x) @safe pure nothrow @nogc { return core.math.sin(x); }
132
133 ///
134 @safe unittest
135 {
136 import std.math.constants : PI;
137 import std.stdio : writefln;
138
someFunc()139 void someFunc()
140 {
141 real x = 30.0;
142 auto result = sin(x * (PI / 180)); // convert degrees to radians
143 writefln("The sine of %s degrees is %s", x, result);
144 }
145 }
146
147 @safe unittest
148 {
149 real function(real) psin = &sin;
150 assert(psin != null);
151 }
152
153 @safe pure nothrow @nogc unittest
154 {
155 import std.math.algebraic : fabs;
156
157 float f = sin(-2.0f);
158 assert(fabs(f - -0.909297f) < .00001);
159
160 double d = sin(-2.0);
161 assert(fabs(d - -0.909297f) < .00001);
162
163 real r = sin(-2.0L);
164 assert(fabs(r - -0.909297f) < .00001);
165 }
166
167 /****************************************************************************
168 * Returns tangent of x. x is in radians.
169 *
170 * $(TABLE_SV
171 * $(TR $(TH x) $(TH tan(x)) $(TH invalid?))
172 * $(TR $(TD $(NAN)) $(TD $(NAN)) $(TD yes))
173 * $(TR $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)0.0) $(TD no))
174 * $(TR $(TD $(PLUSMNINF)) $(TD $(NAN)) $(TD yes))
175 * )
176 */
pragma(inline,true)177 pragma(inline, true)
178 real tan(real x) @safe pure nothrow @nogc
179 {
180 version (InlineAsm_X87)
181 {
182 if (!__ctfe)
183 return tanAsm(x);
184 }
185 return tanImpl(x);
186 }
187
188 /// ditto
pragma(inline,true)189 pragma(inline, true)
190 double tan(double x) @safe pure nothrow @nogc { return __ctfe ? cast(double) tan(cast(real) x) : tanImpl(x); }
191
192 /// ditto
pragma(inline,true)193 pragma(inline, true)
194 float tan(float x) @safe pure nothrow @nogc { return __ctfe ? cast(float) tan(cast(real) x) : tanImpl(x); }
195
196 ///
197 @safe unittest
198 {
199 import std.math.operations : isClose;
200 import std.math.traits : isIdentical;
201 import std.math.constants : PI;
202 import std.math.algebraic : sqrt;
203
204 assert(isIdentical(tan(0.0), 0.0));
205 assert(tan(PI).isClose(0, 0.0, 1e-10));
206 assert(tan(PI / 3).isClose(sqrt(3.0)));
207 }
208
version(InlineAsm_X87)209 version (InlineAsm_X87)
210 private real tanAsm(real x) @trusted pure nothrow @nogc
211 {
212 // Separating `return real.nan` from the asm block on LDC produces unintended
213 // behaviour as additional instructions are generated, invalidating the asm
214 // logic inside the previous block. To circumvent this, we can push rnan
215 // manually by creating an immutable variable in the stack.
216 immutable rnan = real.nan;
217
218 version (X86)
219 {
220 asm pure nothrow @nogc
221 {
222 fld x[EBP] ; // load theta
223 fxam ; // test for oddball values
224 fstsw AX ;
225 sahf ;
226 jc trigerr ; // x is NAN, infinity, or empty
227 // 387's can handle subnormals
228 SC18: fptan ;
229 fstsw AX ;
230 sahf ;
231 jnp Clear1 ; // C2 = 1 (x is out of range)
232
233 // Do argument reduction to bring x into range
234 fldpi ;
235 fxch ;
236 SC17: fprem1 ;
237 fstsw AX ;
238 sahf ;
239 jp SC17 ;
240 fstp ST(1) ; // remove pi from stack
241 jmp SC18 ;
242
243 trigerr:
244 jnp Lret ; // if theta is NAN, return theta
245 fstp ST(0) ; // dump theta
246 fld rnan ; // return rnan
247 jmp Lret ;
248 Clear1:
249 fstp ST(0) ; // dump X, which is always 1
250 Lret:
251 ;
252 }
253 }
254 else version (X86_64)
255 {
256 version (Win64)
257 {
258 asm pure nothrow @nogc
259 {
260 fld real ptr [RCX] ; // load theta
261 }
262 }
263 else
264 {
265 asm pure nothrow @nogc
266 {
267 fld x[RBP] ; // load theta
268 }
269 }
270 asm pure nothrow @nogc
271 {
272 fxam ; // test for oddball values
273 fstsw AX ;
274 test AH,1 ;
275 jnz trigerr ; // x is NAN, infinity, or empty
276 // 387's can handle subnormals
277 SC18: fptan ;
278 fstsw AX ;
279 test AH,4 ;
280 jz Clear1 ; // C2 = 1 (x is out of range)
281
282 // Do argument reduction to bring x into range
283 fldpi ;
284 fxch ;
285 SC17: fprem1 ;
286 fstsw AX ;
287 test AH,4 ;
288 jnz SC17 ;
289 fstp ST(1) ; // remove pi from stack
290 jmp SC18 ;
291
292 trigerr:
293 test AH,4 ;
294 jz Lret ; // if theta is NAN, return theta
295 fstp ST(0) ; // dump theta
296 fld rnan ; // return rnan
297 jmp Lret ;
298 Clear1:
299 fstp ST(0) ; // dump X, which is always 1
300 Lret:
301 ;
302 }
303 }
304 else
305 static assert(0);
306 }
307
tanImpl(T)308 private T tanImpl(T)(T x) @safe pure nothrow @nogc
309 {
310 import std.math : floatTraits, RealFormat;
311 import std.math.constants : PI, PI_4;
312 import std.math.rounding : floor;
313 import std.math.algebraic : poly;
314 import std.math.traits : isInfinity, isNaN, signbit;
315
316 // Coefficients for tan(x) and PI/4 split into three parts.
317 enum realFormat = floatTraits!T.realFormat;
318 static if (realFormat == RealFormat.ieeeQuadruple)
319 {
320 static immutable T[6] P = [
321 2.883414728874239697964612246732416606301E10L,
322 -2.307030822693734879744223131873392503321E9L,
323 5.160188250214037865511600561074819366815E7L,
324 -4.249691853501233575668486667664718192660E5L,
325 1.272297782199996882828849455156962260810E3L,
326 -9.889929415807650724957118893791829849557E-1L
327 ];
328 static immutable T[7] Q = [
329 8.650244186622719093893836740197250197602E10L,
330 -4.152206921457208101480801635640958361612E10L,
331 2.758476078803232151774723646710890525496E9L,
332 -5.733709132766856723608447733926138506824E7L,
333 4.529422062441341616231663543669583527923E5L,
334 -1.317243702830553658702531997959756728291E3L,
335 1.0
336 ];
337
338 enum T P1 =
339 7.853981633974483067550664827649598009884357452392578125E-1L;
340 enum T P2 =
341 2.8605943630549158983813312792950660807511260829685741796657E-18L;
342 enum T P3 =
343 2.1679525325309452561992610065108379921905808E-35L;
344 }
345 else static if (realFormat == RealFormat.ieeeExtended ||
346 realFormat == RealFormat.ieeeDouble)
347 {
348 static immutable T[3] P = [
349 -1.7956525197648487798769E7L,
350 1.1535166483858741613983E6L,
351 -1.3093693918138377764608E4L,
352 ];
353 static immutable T[5] Q = [
354 -5.3869575592945462988123E7L,
355 2.5008380182335791583922E7L,
356 -1.3208923444021096744731E6L,
357 1.3681296347069295467845E4L,
358 1.0000000000000000000000E0L,
359 ];
360
361 enum T P1 = 7.853981554508209228515625E-1L;
362 enum T P2 = 7.946627356147928367136046290398E-9L;
363 enum T P3 = 3.061616997868382943065164830688E-17L;
364 }
365 else static if (realFormat == RealFormat.ieeeSingle)
366 {
367 static immutable T[6] P = [
368 3.33331568548E-1,
369 1.33387994085E-1,
370 5.34112807005E-2,
371 2.44301354525E-2,
372 3.11992232697E-3,
373 9.38540185543E-3,
374 ];
375
376 enum T P1 = 0.78515625;
377 enum T P2 = 2.4187564849853515625E-4;
378 enum T P3 = 3.77489497744594108E-8;
379 }
380 else
381 static assert(0, "no coefficients for tan()");
382
383 // Special cases.
384 if (x == cast(T) 0.0 || isNaN(x))
385 return x;
386 if (isInfinity(x))
387 return T.nan;
388
389 // Make argument positive but save the sign.
390 bool sign = false;
391 if (signbit(x))
392 {
393 sign = true;
394 x = -x;
395 }
396
397 // Compute x mod PI/4.
398 static if (realFormat == RealFormat.ieeeSingle)
399 {
400 enum T FOPI = 4 / PI;
401 int j = cast(int) (FOPI * x);
402 T y = j;
403 T z;
404 }
405 else
406 {
407 T y = floor(x / cast(T) PI_4);
408 // Strip high bits of integer part.
409 enum T highBitsFactor = (realFormat == RealFormat.ieeeDouble ? 0x1p3 : 0x1p4);
410 enum T highBitsInv = 1.0 / highBitsFactor;
411 T z = y * highBitsInv;
412 // Compute y - 2^numHighBits * (y / 2^numHighBits).
413 z = y - highBitsFactor * floor(z);
414
415 // Integer and fraction part modulo one octant.
416 int j = cast(int)(z);
417 }
418
419 // Map zeros and singularities to origin.
420 if (j & 1)
421 {
422 j += 1;
423 y += cast(T) 1.0;
424 }
425
426 z = ((x - y * P1) - y * P2) - y * P3;
427 const T zz = z * z;
428
429 enum T zzThreshold = (realFormat == RealFormat.ieeeSingle ? 1.0e-4L :
430 realFormat == RealFormat.ieeeDouble ? 1.0e-14L : 1.0e-20L);
431 if (zz > zzThreshold)
432 {
433 static if (realFormat == RealFormat.ieeeSingle)
434 y = z + z * (zz * poly(zz, P));
435 else
436 y = z + z * (zz * poly(zz, P) / poly(zz, Q));
437 }
438 else
439 y = z;
440
441 if (j & 2)
442 y = (cast(T) -1.0) / y;
443
444 return (sign) ? -y : y;
445 }
446
447 @safe @nogc nothrow unittest
448 {
testTan(T)449 static void testTan(T)()
450 {
451 import std.math.operations : CommonDefaultFor, isClose, NaN;
452 import std.math.traits : isIdentical, isNaN;
453 import std.math.constants : PI, PI_4;
454
455 // ±0
456 const T zero = 0.0;
457 assert(isIdentical(tan(zero), zero));
458 assert(isIdentical(tan(-zero), -zero));
459 // ±∞
460 const T inf = T.infinity;
461 assert(isNaN(tan(inf)));
462 assert(isNaN(tan(-inf)));
463 // NaN
464 const T specialNaN = NaN(0x0123L);
465 assert(isIdentical(tan(specialNaN), specialNaN));
466
467 static immutable T[2][] vals =
468 [
469 // angle, tan
470 [ .5, .54630248984],
471 [ 1, 1.5574077247],
472 [ 1.5, 14.101419947],
473 [ 2, -2.1850398633],
474 [ 2.5,-.74702229724],
475 [ 3, -.14254654307],
476 [ 3.5, .37458564016],
477 [ 4, 1.1578212823],
478 [ 4.5, 4.6373320546],
479 [ 5, -3.3805150062],
480 [ 5.5,-.99558405221],
481 [ 6, -.29100619138],
482 [ 6.5, .22027720035],
483 [ 10, .64836082746],
484
485 // special angles
486 [ PI_4, 1],
487 //[ PI_2, T.infinity], // PI_2 is not _exactly_ pi/2.
488 [ 3*PI_4, -1],
489 [ PI, 0],
490 [ 5*PI_4, 1],
491 //[ 3*PI_2, -T.infinity],
492 [ 7*PI_4, -1],
493 [ 2*PI, 0],
494 ];
495
496 foreach (ref val; vals)
497 {
498 T x = val[0];
499 T r = val[1];
500 T t = tan(x);
501
502 //printf("tan(%Lg) = %Lg, should be %Lg\n", cast(real) x, cast(real) t, cast(real) r);
503 assert(isClose(r, t, CommonDefaultFor!(T,T), CommonDefaultFor!(T,T)));
504
505 x = -x;
506 r = -r;
507 t = tan(x);
508 //printf("tan(%Lg) = %Lg, should be %Lg\n", cast(real) x, cast(real) t, cast(real) r);
509 assert(isClose(r, t, CommonDefaultFor!(T,T), CommonDefaultFor!(T,T)));
510 }
511 }
512
513 import std.meta : AliasSeq;
514 foreach (T; AliasSeq!(real, double, float))
515 testTan!T();
516
517 import std.math.operations : isClose;
518 import std.math.constants : PI;
519 import std.math.algebraic : sqrt;
520 assert(isClose(tan(PI / 3), sqrt(3.0L), real.sizeof > double.sizeof ? 1e-15 : 1e-14));
521 }
522
523 @safe pure nothrow @nogc unittest
524 {
525 import std.math.algebraic : fabs;
526 import std.math.traits : isNaN;
527
528 float f = tan(-2.0f);
529 assert(fabs(f - 2.18504f) < .00001);
530
531 double d = tan(-2.0);
532 assert(fabs(d - 2.18504f) < .00001);
533
534 real r = tan(-2.0L);
535 assert(fabs(r - 2.18504f) < .00001);
536
537 // Verify correct behavior for large inputs
538 assert(!isNaN(tan(0x1p63)));
539 assert(!isNaN(tan(-0x1p63)));
540 static if (real.mant_dig >= 64)
541 {
542 assert(!isNaN(tan(0x1p300L)));
543 assert(!isNaN(tan(-0x1p300L)));
544 }
545 }
546
547 /***************
548 * Calculates the arc cosine of x,
549 * returning a value ranging from 0 to $(PI).
550 *
551 * $(TABLE_SV
552 * $(TR $(TH x) $(TH acos(x)) $(TH invalid?))
553 * $(TR $(TD $(GT)1.0) $(TD $(NAN)) $(TD yes))
554 * $(TR $(TD $(LT)-1.0) $(TD $(NAN)) $(TD yes))
555 * $(TR $(TD $(NAN)) $(TD $(NAN)) $(TD yes))
556 * )
557 */
acos(real x)558 real acos(real x) @safe pure nothrow @nogc
559 {
560 import core.math : sqrt;
561
562 return atan2(sqrt(1-x*x), x);
563 }
564
565 /// ditto
acos(double x)566 double acos(double x) @safe pure nothrow @nogc { return acos(cast(real) x); }
567
568 /// ditto
acos(float x)569 float acos(float x) @safe pure nothrow @nogc { return acos(cast(real) x); }
570
571 ///
572 @safe unittest
573 {
574 import std.math.operations : isClose;
575 import std.math.traits : isNaN;
576 import std.math.constants : PI;
577
578 assert(acos(0.0).isClose(1.570796327));
579 assert(acos(0.5).isClose(PI / 3));
580 assert(acos(PI).isNaN);
581 }
582
583 @safe @nogc nothrow unittest
584 {
585 import std.math.operations : isClose;
586 import std.math.constants : PI;
587
588 assert(isClose(acos(0.5), PI / 3, real.sizeof > double.sizeof ? 1e-15 : 1e-14));
589 }
590
591 /***************
592 * Calculates the arc sine of x,
593 * returning a value ranging from -$(PI)/2 to $(PI)/2.
594 *
595 * $(TABLE_SV
596 * $(TR $(TH x) $(TH asin(x)) $(TH invalid?))
597 * $(TR $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)0.0) $(TD no))
598 * $(TR $(TD $(GT)1.0) $(TD $(NAN)) $(TD yes))
599 * $(TR $(TD $(LT)-1.0) $(TD $(NAN)) $(TD yes))
600 * )
601 */
asin(real x)602 real asin(real x) @safe pure nothrow @nogc
603 {
604 import core.math : sqrt;
605
606 return atan2(x, sqrt(1-x*x));
607 }
608
609 /// ditto
asin(double x)610 double asin(double x) @safe pure nothrow @nogc { return asin(cast(real) x); }
611
612 /// ditto
asin(float x)613 float asin(float x) @safe pure nothrow @nogc { return asin(cast(real) x); }
614
615 ///
616 @safe unittest
617 {
618 import std.math.operations : isClose;
619 import std.math.traits : isIdentical, isNaN;
620 import std.math.constants : PI;
621
622 assert(isIdentical(asin(0.0), 0.0));
623 assert(asin(0.5).isClose(PI / 6));
624 assert(asin(PI).isNaN);
625 }
626
627 @safe @nogc nothrow unittest
628 {
629 import std.math.operations : isClose;
630 import std.math.constants : PI;
631
632 assert(isClose(asin(0.5), PI / 6, real.sizeof > double.sizeof ? 1e-15 : 1e-14));
633 }
634
635 /***************
636 * Calculates the arc tangent of x,
637 * returning a value ranging from -$(PI)/2 to $(PI)/2.
638 *
639 * $(TABLE_SV
640 * $(TR $(TH x) $(TH atan(x)) $(TH invalid?))
641 * $(TR $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)0.0) $(TD no))
642 * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD $(NAN)) $(TD yes))
643 * )
644 */
pragma(inline,true)645 pragma(inline, true)
646 real atan(real x) @safe pure nothrow @nogc
647 {
648 version (InlineAsm_X87)
649 {
650 if (!__ctfe)
651 return atan2Asm(x, 1.0L);
652 }
653 return atanImpl(x);
654 }
655
656 /// ditto
pragma(inline,true)657 pragma(inline, true)
658 double atan(double x) @safe pure nothrow @nogc { return __ctfe ? cast(double) atan(cast(real) x) : atanImpl(x); }
659
660 /// ditto
pragma(inline,true)661 pragma(inline, true)
662 float atan(float x) @safe pure nothrow @nogc { return __ctfe ? cast(float) atan(cast(real) x) : atanImpl(x); }
663
664 ///
665 @safe unittest
666 {
667 import std.math.operations : isClose;
668 import std.math.traits : isIdentical;
669 import std.math.constants : PI;
670 import std.math.algebraic : sqrt;
671
672 assert(isIdentical(atan(0.0), 0.0));
673 assert(atan(sqrt(3.0)).isClose(PI / 3));
674 }
675
atanImpl(T)676 private T atanImpl(T)(T x) @safe pure nothrow @nogc
677 {
678 import std.math : floatTraits, RealFormat;
679 import std.math.traits : copysign, isInfinity, signbit;
680 import std.math.constants : PI_2, PI_4;
681 import std.math.algebraic : poly;
682
683 // Coefficients for atan(x)
684 enum realFormat = floatTraits!T.realFormat;
685 static if (realFormat == RealFormat.ieeeQuadruple)
686 {
687 static immutable T[9] P = [
688 -6.880597774405940432145577545328795037141E2L,
689 -2.514829758941713674909996882101723647996E3L,
690 -3.696264445691821235400930243493001671932E3L,
691 -2.792272753241044941703278827346430350236E3L,
692 -1.148164399808514330375280133523543970854E3L,
693 -2.497759878476618348858065206895055957104E2L,
694 -2.548067867495502632615671450650071218995E1L,
695 -8.768423468036849091777415076702113400070E-1L,
696 -6.635810778635296712545011270011752799963E-4L
697 ];
698 static immutable T[9] Q = [
699 2.064179332321782129643673263598686441900E3L,
700 8.782996876218210302516194604424986107121E3L,
701 1.547394317752562611786521896296215170819E4L,
702 1.458510242529987155225086911411015961174E4L,
703 7.928572347062145288093560392463784743935E3L,
704 2.494680540950601626662048893678584497900E3L,
705 4.308348370818927353321556740027020068897E2L,
706 3.566239794444800849656497338030115886153E1L,
707 1.0
708 ];
709 }
710 else static if (realFormat == RealFormat.ieeeExtended)
711 {
712 static immutable T[5] P = [
713 -5.0894116899623603312185E1L,
714 -9.9988763777265819915721E1L,
715 -6.3976888655834347413154E1L,
716 -1.4683508633175792446076E1L,
717 -8.6863818178092187535440E-1L,
718 ];
719 static immutable T[6] Q = [
720 1.5268235069887081006606E2L,
721 3.9157570175111990631099E2L,
722 3.6144079386152023162701E2L,
723 1.4399096122250781605352E2L,
724 2.2981886733594175366172E1L,
725 1.0000000000000000000000E0L,
726 ];
727 }
728 else static if (realFormat == RealFormat.ieeeDouble)
729 {
730 static immutable T[5] P = [
731 -6.485021904942025371773E1L,
732 -1.228866684490136173410E2L,
733 -7.500855792314704667340E1L,
734 -1.615753718733365076637E1L,
735 -8.750608600031904122785E-1L,
736 ];
737 static immutable T[6] Q = [
738 1.945506571482613964425E2L,
739 4.853903996359136964868E2L,
740 4.328810604912902668951E2L,
741 1.650270098316988542046E2L,
742 2.485846490142306297962E1L,
743 1.000000000000000000000E0L,
744 ];
745
746 enum T MOREBITS = 6.123233995736765886130E-17L;
747 }
748 else static if (realFormat == RealFormat.ieeeSingle)
749 {
750 static immutable T[4] P = [
751 -3.33329491539E-1,
752 1.99777106478E-1,
753 -1.38776856032E-1,
754 8.05374449538E-2,
755 ];
756 }
757 else
758 static assert(0, "no coefficients for atan()");
759
760 // tan(PI/8)
761 enum T TAN_PI_8 = 0.414213562373095048801688724209698078569672L;
762 // tan(3 * PI/8)
763 enum T TAN3_PI_8 = 2.414213562373095048801688724209698078569672L;
764
765 // Special cases.
766 if (x == cast(T) 0.0)
767 return x;
768 if (isInfinity(x))
769 return copysign(cast(T) PI_2, x);
770
771 // Make argument positive but save the sign.
772 bool sign = false;
773 if (signbit(x))
774 {
775 sign = true;
776 x = -x;
777 }
778
779 static if (realFormat == RealFormat.ieeeDouble) // special case for double precision
780 {
781 short flag = 0;
782 T y;
783 if (x > TAN3_PI_8)
784 {
785 y = PI_2;
786 flag = 1;
787 x = -(1.0 / x);
788 }
789 else if (x <= 0.66)
790 {
791 y = 0.0;
792 }
793 else
794 {
795 y = PI_4;
796 flag = 2;
797 x = (x - 1.0)/(x + 1.0);
798 }
799
800 T z = x * x;
801 z = z * poly(z, P) / poly(z, Q);
802 z = x * z + x;
803 if (flag == 2)
804 z += 0.5 * MOREBITS;
805 else if (flag == 1)
806 z += MOREBITS;
807 y = y + z;
808 }
809 else
810 {
811 // Range reduction.
812 T y;
813 if (x > TAN3_PI_8)
814 {
815 y = PI_2;
816 x = -((cast(T) 1.0) / x);
817 }
818 else if (x > TAN_PI_8)
819 {
820 y = PI_4;
821 x = (x - cast(T) 1.0)/(x + cast(T) 1.0);
822 }
823 else
824 y = 0.0;
825
826 // Rational form in x^^2.
827 const T z = x * x;
828 static if (realFormat == RealFormat.ieeeSingle)
829 y += poly(z, P) * z * x + x;
830 else
831 y = y + (poly(z, P) / poly(z, Q)) * z * x + x;
832 }
833
834 return (sign) ? -y : y;
835 }
836
837 @safe @nogc nothrow unittest
838 {
testAtan(T)839 static void testAtan(T)()
840 {
841 import std.math.operations : CommonDefaultFor, isClose, NaN;
842 import std.math.traits : isIdentical;
843 import std.math.constants : PI_2, PI_4;
844
845 // ±0
846 const T zero = 0.0;
847 assert(isIdentical(atan(zero), zero));
848 assert(isIdentical(atan(-zero), -zero));
849 // ±∞
850 const T inf = T.infinity;
851 assert(isClose(atan(inf), cast(T) PI_2));
852 assert(isClose(atan(-inf), cast(T) -PI_2));
853 // NaN
854 const T specialNaN = NaN(0x0123L);
855 assert(isIdentical(atan(specialNaN), specialNaN));
856
857 static immutable T[2][] vals =
858 [
859 // x, atan(x)
860 [ 0.25, 0.24497866313 ],
861 [ 0.5, 0.46364760900 ],
862 [ 1, PI_4 ],
863 [ 1.5, 0.98279372325 ],
864 [ 10, 1.47112767430 ],
865 ];
866
867 foreach (ref val; vals)
868 {
869 T x = val[0];
870 T r = val[1];
871 T a = atan(x);
872
873 //printf("atan(%Lg) = %Lg, should be %Lg\n", cast(real) x, cast(real) a, cast(real) r);
874 assert(isClose(r, a, CommonDefaultFor!(T,T), CommonDefaultFor!(T,T)));
875
876 x = -x;
877 r = -r;
878 a = atan(x);
879 //printf("atan(%Lg) = %Lg, should be %Lg\n", cast(real) x, cast(real) a, cast(real) r);
880 assert(isClose(r, a, CommonDefaultFor!(T,T), CommonDefaultFor!(T,T)));
881 }
882 }
883
884 import std.meta : AliasSeq;
885 foreach (T; AliasSeq!(real, double, float))
886 testAtan!T();
887
888 import std.math.operations : isClose;
889 import std.math.algebraic : sqrt;
890 import std.math.constants : PI;
891 assert(isClose(atan(sqrt(3.0L)), PI / 3, real.sizeof > double.sizeof ? 1e-15 : 1e-14));
892 }
893
894 /***************
895 * Calculates the arc tangent of y / x,
896 * returning a value ranging from -$(PI) to $(PI).
897 *
898 * $(TABLE_SV
899 * $(TR $(TH y) $(TH x) $(TH atan(y, x)))
900 * $(TR $(TD $(NAN)) $(TD anything) $(TD $(NAN)) )
901 * $(TR $(TD anything) $(TD $(NAN)) $(TD $(NAN)) )
902 * $(TR $(TD $(PLUSMN)0.0) $(TD $(GT)0.0) $(TD $(PLUSMN)0.0) )
903 * $(TR $(TD $(PLUSMN)0.0) $(TD +0.0) $(TD $(PLUSMN)0.0) )
904 * $(TR $(TD $(PLUSMN)0.0) $(TD $(LT)0.0) $(TD $(PLUSMN)$(PI)))
905 * $(TR $(TD $(PLUSMN)0.0) $(TD -0.0) $(TD $(PLUSMN)$(PI)))
906 * $(TR $(TD $(GT)0.0) $(TD $(PLUSMN)0.0) $(TD $(PI)/2) )
907 * $(TR $(TD $(LT)0.0) $(TD $(PLUSMN)0.0) $(TD -$(PI)/2) )
908 * $(TR $(TD $(GT)0.0) $(TD $(INFIN)) $(TD $(PLUSMN)0.0) )
909 * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD anything) $(TD $(PLUSMN)$(PI)/2))
910 * $(TR $(TD $(GT)0.0) $(TD -$(INFIN)) $(TD $(PLUSMN)$(PI)) )
911 * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD $(INFIN)) $(TD $(PLUSMN)$(PI)/4))
912 * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD -$(INFIN)) $(TD $(PLUSMN)3$(PI)/4))
913 * )
914 */
pragma(inline,true)915 pragma(inline, true)
916 real atan2(real y, real x) @trusted pure nothrow @nogc // TODO: @safe
917 {
918 version (InlineAsm_X87)
919 {
920 if (!__ctfe)
921 return atan2Asm(y, x);
922 }
923 return atan2Impl(y, x);
924 }
925
926 /// ditto
pragma(inline,true)927 pragma(inline, true)
928 double atan2(double y, double x) @safe pure nothrow @nogc
929 {
930 return __ctfe ? cast(double) atan2(cast(real) y, cast(real) x) : atan2Impl(y, x);
931 }
932
933 /// ditto
pragma(inline,true)934 pragma(inline, true)
935 float atan2(float y, float x) @safe pure nothrow @nogc
936 {
937 return __ctfe ? cast(float) atan2(cast(real) y, cast(real) x) : atan2Impl(y, x);
938 }
939
940 ///
941 @safe unittest
942 {
943 import std.math.operations : isClose;
944 import std.math.constants : PI;
945 import std.math.algebraic : sqrt;
946
947 assert(atan2(1.0, sqrt(3.0)).isClose(PI / 6));
948 }
949
version(InlineAsm_X87)950 version (InlineAsm_X87)
951 private real atan2Asm(real y, real x) @trusted pure nothrow @nogc
952 {
953 version (Win64)
954 {
955 asm pure nothrow @nogc {
956 naked;
957 fld real ptr [RDX]; // y
958 fld real ptr [RCX]; // x
959 fpatan;
960 ret;
961 }
962 }
963 else
964 {
965 asm pure nothrow @nogc {
966 fld y;
967 fld x;
968 fpatan;
969 }
970 }
971 }
972
atan2Impl(T)973 private T atan2Impl(T)(T y, T x) @safe pure nothrow @nogc
974 {
975 import std.math.traits : copysign, isInfinity, isNaN, signbit;
976 import std.math.constants : PI, PI_2, PI_4;
977
978 // Special cases.
979 if (isNaN(x) || isNaN(y))
980 return T.nan;
981 if (y == cast(T) 0.0)
982 {
983 if (x >= 0 && !signbit(x))
984 return copysign(0, y);
985 else
986 return copysign(cast(T) PI, y);
987 }
988 if (x == cast(T) 0.0)
989 return copysign(cast(T) PI_2, y);
990 if (isInfinity(x))
991 {
992 if (signbit(x))
993 {
994 if (isInfinity(y))
995 return copysign(3 * cast(T) PI_4, y);
996 else
997 return copysign(cast(T) PI, y);
998 }
999 else
1000 {
1001 if (isInfinity(y))
1002 return copysign(cast(T) PI_4, y);
1003 else
1004 return copysign(cast(T) 0.0, y);
1005 }
1006 }
1007 if (isInfinity(y))
1008 return copysign(cast(T) PI_2, y);
1009
1010 // Call atan and determine the quadrant.
1011 T z = atan(y / x);
1012
1013 if (signbit(x))
1014 {
1015 if (signbit(y))
1016 z = z - cast(T) PI;
1017 else
1018 z = z + cast(T) PI;
1019 }
1020
1021 if (z == cast(T) 0.0)
1022 return copysign(z, y);
1023
1024 return z;
1025 }
1026
1027 @safe @nogc nothrow unittest
1028 {
testAtan2(T)1029 static void testAtan2(T)()
1030 {
1031 import std.math.operations : isClose;
1032 import std.math.traits : isIdentical, isNaN;
1033 import std.math.constants : PI, PI_2, PI_4;
1034
1035 // NaN
1036 const T nan = T.nan;
1037 assert(isNaN(atan2(nan, cast(T) 1)));
1038 assert(isNaN(atan2(cast(T) 1, nan)));
1039
1040 const T inf = T.infinity;
1041 static immutable T[3][] vals =
1042 [
1043 // y, x, atan2(y, x)
1044
1045 // ±0
1046 [ 0.0, 1.0, 0.0 ],
1047 [ -0.0, 1.0, -0.0 ],
1048 [ 0.0, 0.0, 0.0 ],
1049 [ -0.0, 0.0, -0.0 ],
1050 [ 0.0, -1.0, PI ],
1051 [ -0.0, -1.0, -PI ],
1052 [ 0.0, -0.0, PI ],
1053 [ -0.0, -0.0, -PI ],
1054 [ 1.0, 0.0, PI_2 ],
1055 [ 1.0, -0.0, PI_2 ],
1056 [ -1.0, 0.0, -PI_2 ],
1057 [ -1.0, -0.0, -PI_2 ],
1058
1059 // ±∞
1060 [ 1.0, inf, 0.0 ],
1061 [ -1.0, inf, -0.0 ],
1062 [ 1.0, -inf, PI ],
1063 [ -1.0, -inf, -PI ],
1064 [ inf, 1.0, PI_2 ],
1065 [ inf, -1.0, PI_2 ],
1066 [ -inf, 1.0, -PI_2 ],
1067 [ -inf, -1.0, -PI_2 ],
1068 [ inf, inf, PI_4 ],
1069 [ -inf, inf, -PI_4 ],
1070 [ inf, -inf, 3 * PI_4 ],
1071 [ -inf, -inf, -3 * PI_4 ],
1072
1073 [ 1.0, 1.0, PI_4 ],
1074 [ -2.0, 2.0, -PI_4 ],
1075 [ 3.0, -3.0, 3 * PI_4 ],
1076 [ -4.0, -4.0, -3 * PI_4 ],
1077
1078 [ 0.75, 0.25, 1.249045772398 ],
1079 [ -0.5, 0.375, -0.927295218002 ],
1080 [ 0.5, -0.125, 1.815774989922 ],
1081 [ -0.75, -0.5, -2.158798930342 ],
1082 ];
1083
1084 foreach (ref val; vals)
1085 {
1086 const T y = val[0];
1087 const T x = val[1];
1088 const T r = val[2];
1089 const T a = atan2(y, x);
1090
1091 //printf("atan2(%Lg, %Lg) = %Lg, should be %Lg\n", cast(real) y, cast(real) x, cast(real) a, cast(real) r);
1092 if (r == 0)
1093 assert(isIdentical(r, a)); // check sign
1094 else
1095 assert(isClose(r, a));
1096 }
1097 }
1098
1099 import std.meta : AliasSeq;
1100 foreach (T; AliasSeq!(real, double, float))
1101 testAtan2!T();
1102
1103 import std.math.operations : isClose;
1104 import std.math.algebraic : sqrt;
1105 import std.math.constants : PI;
1106 assert(isClose(atan2(1.0L, sqrt(3.0L)), PI / 6, real.sizeof > double.sizeof ? 1e-15 : 1e-14));
1107 }
1108
1109 /***********************************
1110 * Calculates the hyperbolic cosine of x.
1111 *
1112 * $(TABLE_SV
1113 * $(TR $(TH x) $(TH cosh(x)) $(TH invalid?))
1114 * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD $(PLUSMN)0.0) $(TD no) )
1115 * )
1116 */
cosh(real x)1117 real cosh(real x) @safe pure nothrow @nogc
1118 {
1119 import std.math.exponential : exp;
1120
1121 // cosh = (exp(x)+exp(-x))/2.
1122 // The naive implementation works correctly.
1123 const real y = exp(x);
1124 return (y + 1.0/y) * 0.5;
1125 }
1126
1127 /// ditto
cosh(double x)1128 double cosh(double x) @safe pure nothrow @nogc { return cosh(cast(real) x); }
1129
1130 /// ditto
cosh(float x)1131 float cosh(float x) @safe pure nothrow @nogc { return cosh(cast(real) x); }
1132
1133 ///
1134 @safe unittest
1135 {
1136 import std.math.constants : E;
1137 import std.math.operations : isClose;
1138
1139 assert(cosh(0.0) == 1.0);
1140 assert(cosh(1.0).isClose((E + 1.0 / E) / 2));
1141 }
1142
1143 @safe @nogc nothrow unittest
1144 {
1145 import std.math.constants : E;
1146 import std.math.operations : isClose;
1147
1148 assert(isClose(cosh(1.0), (E + 1.0 / E) / 2, real.sizeof > double.sizeof ? 1e-15 : 1e-14));
1149 }
1150
1151 /***********************************
1152 * Calculates the hyperbolic sine of x.
1153 *
1154 * $(TABLE_SV
1155 * $(TR $(TH x) $(TH sinh(x)) $(TH invalid?))
1156 * $(TR $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)0.0) $(TD no))
1157 * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD $(PLUSMN)$(INFIN)) $(TD no))
1158 * )
1159 */
sinh(real x)1160 real sinh(real x) @safe pure nothrow @nogc { return _sinh(x); }
1161
1162 /// ditto
sinh(double x)1163 double sinh(double x) @safe pure nothrow @nogc { return _sinh(x); }
1164
1165 /// ditto
sinh(float x)1166 float sinh(float x) @safe pure nothrow @nogc { return _sinh(x); }
1167
1168 ///
1169 @safe unittest
1170 {
1171 import std.math.constants : E;
1172 import std.math.operations : isClose;
1173 import std.math.traits : isIdentical;
1174
1175 enum sinh1 = (E - 1.0 / E) / 2;
1176 import std.meta : AliasSeq;
1177 static foreach (F; AliasSeq!(float, double, real))
1178 {
1179 assert(isIdentical(sinh(F(0.0)), F(0.0)));
1180 assert(sinh(F(1.0)).isClose(F(sinh1)));
1181 }
1182 }
1183
_sinh(F)1184 private F _sinh(F)(F x)
1185 {
1186 import std.math.traits : copysign;
1187 import std.math.exponential : exp, expm1;
1188 import core.math : fabs;
1189 import std.math.constants : LN2;
1190
1191 // sinh(x) = (exp(x)-exp(-x))/2;
1192 // Very large arguments could cause an overflow, but
1193 // the maximum value of x for which exp(x) + exp(-x)) != exp(x)
1194 // is x = 0.5 * (real.mant_dig) * LN2. // = 22.1807 for real80.
1195 if (fabs(x) > F.mant_dig * F(LN2))
1196 {
1197 return copysign(F(0.5) * exp(fabs(x)), x);
1198 }
1199
1200 const y = expm1(x);
1201 return F(0.5) * y / (y+1) * (y+2);
1202 }
1203
1204 @safe @nogc nothrow unittest
1205 {
1206 import std.math.constants : E;
1207 import std.math.operations : isClose;
1208
1209 assert(isClose(sinh(1.0L), real((E - 1.0 / E) / 2), real.sizeof > double.sizeof ? 1e-15 : 1e-14));
1210 }
1211 /***********************************
1212 * Calculates the hyperbolic tangent of x.
1213 *
1214 * $(TABLE_SV
1215 * $(TR $(TH x) $(TH tanh(x)) $(TH invalid?))
1216 * $(TR $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)0.0) $(TD no) )
1217 * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD $(PLUSMN)1.0) $(TD no))
1218 * )
1219 */
tanh(real x)1220 real tanh(real x) @safe pure nothrow @nogc { return _tanh(x); }
1221
1222 /// ditto
tanh(double x)1223 double tanh(double x) @safe pure nothrow @nogc { return _tanh(x); }
1224
1225 /// ditto
tanh(float x)1226 float tanh(float x) @safe pure nothrow @nogc { return _tanh(x); }
1227
1228 ///
1229 @safe unittest
1230 {
1231 import std.math.operations : isClose;
1232 import std.math.traits : isIdentical;
1233
1234 assert(isIdentical(tanh(0.0), 0.0));
1235 assert(tanh(1.0).isClose(sinh(1.0) / cosh(1.0)));
1236 }
1237
_tanh(F)1238 private F _tanh(F)(F x)
1239 {
1240 import std.math.traits : copysign;
1241 import std.math.exponential : expm1;
1242 import core.math : fabs;
1243 import std.math.constants : LN2;
1244
1245 // tanh(x) = (exp(x) - exp(-x))/(exp(x)+exp(-x))
1246 if (fabs(x) > F.mant_dig * F(LN2))
1247 {
1248 return copysign(1, x);
1249 }
1250
1251 const y = expm1(2*x);
1252 return y / (y + 2);
1253 }
1254
1255 @safe @nogc nothrow unittest
1256 {
1257 import std.math.operations : isClose;
1258
1259 assert(isClose(tanh(1.0L), sinh(1.0L) / cosh(1.0L), real.sizeof > double.sizeof ? 1e-15 : 1e-14));
1260 }
1261
1262 /***********************************
1263 * Calculates the inverse hyperbolic cosine of x.
1264 *
1265 * Mathematically, acosh(x) = log(x + sqrt( x*x - 1))
1266 *
1267 * $(TABLE_DOMRG
1268 * $(DOMAIN 1..$(INFIN)),
1269 * $(RANGE 0..$(INFIN))
1270 * )
1271 *
1272 * $(TABLE_SV
1273 * $(SVH x, acosh(x) )
1274 * $(SV $(NAN), $(NAN) )
1275 * $(SV $(LT)1, $(NAN) )
1276 * $(SV 1, 0 )
1277 * $(SV +$(INFIN),+$(INFIN))
1278 * )
1279 */
acosh(real x)1280 real acosh(real x) @safe pure nothrow @nogc { return _acosh(x); }
1281
1282 /// ditto
acosh(double x)1283 double acosh(double x) @safe pure nothrow @nogc { return _acosh(x); }
1284
1285 /// ditto
acosh(float x)1286 float acosh(float x) @safe pure nothrow @nogc { return _acosh(x); }
1287
1288 ///
1289 @safe @nogc nothrow unittest
1290 {
1291 import std.math.traits : isIdentical, isNaN;
1292
1293 assert(isNaN(acosh(0.9)));
1294 assert(isNaN(acosh(real.nan)));
1295 assert(isIdentical(acosh(1.0), 0.0));
1296 assert(acosh(real.infinity) == real.infinity);
1297 assert(isNaN(acosh(0.5)));
1298 }
1299
_acosh(F)1300 private F _acosh(F)(F x) @safe pure nothrow @nogc
1301 {
1302 import std.math.constants : LN2;
1303 import std.math.exponential : log;
1304 import core.math : sqrt;
1305
1306 if (x > 1/F.epsilon)
1307 return F(LN2) + log(x);
1308 else
1309 return log(x + sqrt(x*x - 1));
1310 }
1311
1312 @safe @nogc nothrow unittest
1313 {
1314 import std.math.operations : isClose;
1315
1316 assert(isClose(acosh(cosh(3.0L)), 3.0L, real.sizeof > double.sizeof ? 1e-15 : 1e-14));
1317 }
1318
1319 /***********************************
1320 * Calculates the inverse hyperbolic sine of x.
1321 *
1322 * Mathematically,
1323 * ---------------
1324 * asinh(x) = log( x + sqrt( x*x + 1 )) // if x >= +0
1325 * asinh(x) = -log(-x + sqrt( x*x + 1 )) // if x <= -0
1326 * -------------
1327 *
1328 * $(TABLE_SV
1329 * $(SVH x, asinh(x) )
1330 * $(SV $(NAN), $(NAN) )
1331 * $(SV $(PLUSMN)0, $(PLUSMN)0 )
1332 * $(SV $(PLUSMN)$(INFIN),$(PLUSMN)$(INFIN))
1333 * )
1334 */
asinh(real x)1335 real asinh(real x) @safe pure nothrow @nogc { return _asinh(x); }
1336
1337 /// ditto
asinh(double x)1338 double asinh(double x) @safe pure nothrow @nogc { return _asinh(x); }
1339
1340 /// ditto
asinh(float x)1341 float asinh(float x) @safe pure nothrow @nogc { return _asinh(x); }
1342
1343 ///
1344 @safe @nogc nothrow unittest
1345 {
1346 import std.math.traits : isIdentical, isNaN;
1347
1348 assert(isIdentical(asinh(0.0), 0.0));
1349 assert(isIdentical(asinh(-0.0), -0.0));
1350 assert(asinh(real.infinity) == real.infinity);
1351 assert(asinh(-real.infinity) == -real.infinity);
1352 assert(isNaN(asinh(real.nan)));
1353 }
1354
_asinh(F)1355 private F _asinh(F)(F x)
1356 {
1357 import std.math.traits : copysign;
1358 import core.math : fabs, sqrt;
1359 import std.math.exponential : log, log1p;
1360 import std.math.constants : LN2;
1361
1362 return (fabs(x) > 1 / F.epsilon)
1363 // beyond this point, x*x + 1 == x*x
1364 ? copysign(F(LN2) + log(fabs(x)), x)
1365 // sqrt(x*x + 1) == 1 + x * x / ( 1 + sqrt(x*x + 1) )
1366 : copysign(log1p(fabs(x) + x*x / (1 + sqrt(x*x + 1)) ), x);
1367 }
1368
1369 @safe unittest
1370 {
1371 import std.math.operations : isClose;
1372
1373 assert(isClose(asinh(sinh(3.0L)), 3.0L, real.sizeof > double.sizeof ? 1e-15 : 1e-14));
1374 }
1375
1376 /***********************************
1377 * Calculates the inverse hyperbolic tangent of x,
1378 * returning a value from ranging from -1 to 1.
1379 *
1380 * Mathematically, atanh(x) = log( (1+x)/(1-x) ) / 2
1381 *
1382 * $(TABLE_DOMRG
1383 * $(DOMAIN -$(INFIN)..$(INFIN)),
1384 * $(RANGE -1 .. 1)
1385 * )
1386 * $(BR)
1387 * $(TABLE_SV
1388 * $(SVH x, acosh(x) )
1389 * $(SV $(NAN), $(NAN) )
1390 * $(SV $(PLUSMN)0, $(PLUSMN)0)
1391 * $(SV -$(INFIN), -0)
1392 * )
1393 */
atanh(real x)1394 real atanh(real x) @safe pure nothrow @nogc
1395 {
1396 import std.math.exponential : log1p;
1397
1398 // log( (1+x)/(1-x) ) == log ( 1 + (2*x)/(1-x) )
1399 return 0.5 * log1p( 2 * x / (1 - x) );
1400 }
1401
1402 /// ditto
atanh(double x)1403 double atanh(double x) @safe pure nothrow @nogc { return atanh(cast(real) x); }
1404
1405 /// ditto
atanh(float x)1406 float atanh(float x) @safe pure nothrow @nogc { return atanh(cast(real) x); }
1407
1408 ///
1409 @safe @nogc nothrow unittest
1410 {
1411 import std.math.traits : isIdentical, isNaN;
1412
1413 assert(isIdentical(atanh(0.0), 0.0));
1414 assert(isIdentical(atanh(-0.0),-0.0));
1415 assert(isNaN(atanh(real.nan)));
1416 assert(isNaN(atanh(-real.infinity)));
1417 assert(atanh(0.0) == 0);
1418 }
1419
1420 @safe unittest
1421 {
1422 import std.math.operations : isClose;
1423
1424 assert(isClose(atanh(tanh(0.5L)), 0.5, real.sizeof > double.sizeof ? 1e-15 : 1e-14));
1425 }
1426