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