1 // Written in the D programming language 2 3 /** 4 * Floating-point decimal mathematical functions. 5 * 6 * An implementation of the 7 * General Decimal Arithmetic Specification. 8 * 9 * Authors: Paul D. Anderson 10 * 11 * Copyright: Copyright 2009-2016 by Paul D. Anderson. 12 * 13 * License: <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a> 14 * 15 * Standards: Conforms to the 16 * General Decimal Arithmetic Specification, 17 * Version 1.70, (25 March 2009). 18 */ 19 20 module eris.decimal.math; 21 22 import std.stdio; // for test only 23 24 import eris.decimal; 25 26 unittest { 27 writeln(" math tests "); 28 writeln("=========================="); 29 } 30 31 version(unittest) { 32 import std.stdio; 33 // import eris.decimal.asserts; 34 import eris.decimal.test; 35 } 36 37 /* 38 TODO: For each function -- 39 1. Ensure algorithm is properly implemented. 40 2. Ensure context flags are being set properly. 41 3. Ensure function passes GDA tests. 42 4. Determine additional tests needed and implement them. 43 5. Ensure all special cases are enumerated. 44 6. Automate all tests, if possible. 45 7. Determine what effect the context has on the function and 46 if it should be explained. 47 8. Ensure all documentation is complete: 48 a. Header - description, inputs, return value(s) 49 b. Code - variables, control statements, branches, return points. 50 9. Move most tests to the test module. 51 */ 52 53 /* 54 mixin template checkNaN() { 55 if (x.isNaN) { 56 contextFlags.set(INVALID_OPERATION); 57 return T.nan; 58 } 59 }*/ 60 61 //-------------------------------- 62 // ROUNDING 63 //-------------------------------- 64 65 unittest 66 { 67 writeln("-------- rounding --------"); 68 } 69 /** 70 * Rounds the argument to the nearest integer. If the argument is exactly 71 * half-way between two integers the even integer is returned. 72 */ 73 public D rint(D)(D num) 74 if (isDecimal!D) 75 { 76 return roundToInt(num, HALF_EVEN); 77 } 78 79 unittest 80 { // rint 81 static struct S { D9 num; D9 expect; } 82 S[] s = 83 [ 84 { "2.1", "2" }, 85 { "100", "100" }, 86 { "100.0", "100" }, 87 { "101.5", "102" }, 88 { "-101.5", "-102" }, 89 { "10E+5", "1.0E+6" }, 90 { "7.89E+77", "7.89E+77" }, 91 { "-Inf", "-Infinity" }, 92 { "2.1", 2 }, 93 { "2.5", 2 }, 94 { "3.5", 4 }, 95 { "2.9", 3 }, 96 { "-2.1", -2 }, 97 { "-2.9", -3 }, 98 { "-2.5", -2 }, 99 ]; 100 auto f = FunctionTest!(S,D9)("rint"); 101 foreach (t; s) f.test(t, rint(t.num)); 102 writefln(f.report); 103 } 104 105 /** 106 * Returns the nearest integer less than or equal to the argument. 107 * Rounds toward negative infinity. 108 */ 109 public T floor(T)(T x) { 110 return roundToInt(x, FLOOR); 111 } 112 113 unittest 114 { // floor 115 static struct S { TD x; TD expect; } 116 S[] s = 117 [ 118 { "2.1", 2 }, 119 { "2.5", 2 }, 120 { "3.5", 3 }, 121 { "2.9", 2 }, 122 { "-2.1", -3 }, 123 { "-2.9", -3 }, 124 { "-2.5", -3 }, 125 ]; 126 auto f = FunctionTest!(S,TD)("floor"); 127 foreach (t; s) f.test(t, floor(t.x)); 128 writefln(f.report); 129 } 130 131 /** 132 * Returns the nearest integer greater than or equal to the argument. 133 * Rounds toward positive infinity. 134 */ 135 public T ceil(T)(T x) { 136 return roundToInt(x, CEILING); 137 } 138 139 unittest 140 { // ceil 141 static struct S { TD x; TD expect; } 142 S[] s = 143 [ 144 { "2.1", 3 }, 145 { "2.5", 3 }, 146 { "3.5", 4 }, 147 { "2.9", 3 }, 148 { "-2.1", -2 }, 149 { "-2.9", -2 }, 150 { "-2.5", -2 }, 151 ]; 152 auto f = FunctionTest!(S,TD)("ceil"); 153 foreach (t; s) f.test(t, ceil(t.x)); 154 writefln(f.report); 155 } 156 157 /** 158 * Returns the truncated argument. 159 * Rounds toward zero. 160 */ 161 public T trunc(T)(T x) { 162 return roundToInt(x, ROUND_DOWN); 163 } 164 165 unittest 166 { // trunc 167 static struct S { TD x; TD expect; } 168 S[] s = 169 [ 170 { "2.1", 2 }, 171 { "2.5", 2 }, 172 { "3.5", 3 }, 173 { "2.9", 2 }, 174 { "-2.1", -2 }, 175 { "-2.9", -2 }, 176 { "-2.5", -2 }, 177 ]; 178 auto f = FunctionTest!(S,TD)("trunc"); 179 foreach (t; s) f.test(t, trunc(t.x)); 180 writefln(f.report); 181 } 182 183 /** 184 * Returns the nearest integer value. If the value is greater (less) than 185 * the maximum (minimum) int value the maximum (minimum) value is returned. 186 * The value is rounded based on the specified rounding mode. The default 187 * mode is half-even. 188 */ 189 public int toInt(T)(T x, Round mode = HALF_EVEN) 190 { 191 if (x.isNaN) 192 { 193 throw new InvalidOperationException("NaN cannot be converted to int"); 194 } 195 if (x.isInfinite) 196 { 197 return x.isNegative ? int.min : int.max; 198 } 199 return toBigInt(x, mode).toInt; 200 } 201 202 /** 203 * Returns the nearest long value. If the value is greater (less) than 204 * the maximum (minimum) long value the maximum (minimum) value is returned. 205 * The value is rounded based on the specified rounding mode. The default 206 * mode is half-even. 207 */ 208 public long toLong(T)(T x, 209 Round mode = HALF_EVEN) if (isDecimal!T) 210 { 211 if (x.isNaN) 212 { 213 throw new InvalidOperationException("NaN cannot be converted to long"); 214 } 215 if (x.isInfinite) 216 { 217 return x.isNegative ? long.min : long.max; 218 } 219 return toBigInt(x, mode).toLong; 220 } 221 222 // FIXTHIS: toBigInt doesn't work? 223 /** 224 * Returns the nearest extended integer value. 225 * The value is rounded based on the specified rounding mode. The default 226 * mode is half-even. 227 */ 228 public BigInt toBigInt(T)(T x, Round mode = HALF_EVEN) 229 { 230 if (x.isNaN) 231 { 232 throw new InvalidOperationException("NaN cannot be converted to BigInt"); 233 } 234 if (x.isInfinite) 235 { 236 return x.isNegative ? -T.max.coff : T.max.coff; 237 } 238 if (x.expo != 0) 239 { 240 return roundToInt(x, mode).coff; 241 } 242 return x.coff; 243 } 244 245 //-------------------------------- 246 // string mixins 247 //-------------------------------- 248 249 /*template Constant(string name) 250 { 251 const char[] Constant = 252 "/// Returns the value of the constant at the specified precision. 253 public T " ~ name ~ "(T)(int precision = T.precision) if (isDecimal!T) 254 { 255 Context context = Context(precision, T.maxExpo, T.mode); 256 static T value; 257 static int lastPrecision = 0; 258 if (precision == lastPrecision) return value; 259 if (precision > lastPrecision) { 260 value = eris.decimal.math." ~ name ~ "!T(context); 261 lastPrecision = precision; 262 } 263 return round(value, precision); 264 }"; 265 }*/ 266 267 /*template UnaryFunction(string name) 268 { 269 const char[] UnaryFunction = 270 271 "/// Returns the value of the function at the specified precision. 272 public T " ~ name ~ "(T, U:int)(T x, U precision = T.precision) if (isDecimal!T) 273 { 274 if (x.isNaN) { 275 contextFlags.set(INVALID_OPERATION); 276 return T.nan; 277 } 278 Context context = Context(precision, T.maxExpo, HALF_EVEN); 279 return " ~ name ~ "!T(x, context); 280 } 281 282 /// Returns the value of the function at the specified precision. 283 public T " ~ name ~ "(T,S:string)(S str, int precision = T.precision) if (isDecimal!T) { 284 T x = T(str); 285 return " ~ name ~ "!T(x, precision); 286 } 287 288 /// Returns the value of the function at the specified precision. 289 public T " ~ name ~ "(T,L:long)(L n, int precision = T.precision) if (isDecimal!T) { 290 T x = T(n); 291 return " ~ name ~ "!T(x, precision); 292 } 293 294 /// Returns the value of the function at the specified precision. 295 public T " ~ name ~ "(T,R:real)(R a, int precision = T.precision) if (isDecimal!T){ 296 T x = T(a); 297 return " ~ name ~ "!T(x, precision); 298 }"; 299 } 300 */ 301 302 template UnaryFunction(string name) 303 { 304 const char[] UnaryFunction = 305 306 "/// Returns the value of the function at the specified precision. 307 public T " ~ name ~ "(T)(T x, int precision = T.precision) if (isDecimal!T) 308 { 309 if (x.isNaN) 310 { 311 contextFlags.set(INVALID_OPERATION); 312 return T.nan; 313 } 314 Context context = Context(precision, T.maxExpo, HALF_EVEN); 315 return " ~ name ~ "!T(x, context); 316 } 317 318 /// Returns the value of the function at the specified precision. 319 public T " ~ name ~ "(T,U)(U num, int precision) 320 if (isDecimal!T && isConvertible!U) 321 { 322 T x = T(num); 323 return " ~ name ~ "!T(x, precision); 324 }"; 325 } 326 327 template BinaryFunction(string name) 328 { 329 const char[] BinaryFunction = 330 331 "/// Returns the value of the function at the specified precision. 332 public T " ~ name ~ "(T)(T x, T y, int precision = T.precision) 333 if (isDecimal!T) 334 { 335 if (x.isNaN || y.isNaN) 336 { 337 contextFlags.set(INVALID_OPERATION); 338 return T.nan; 339 } 340 Context context = Context(precision, T.maxExpo, HALF_EVEN); 341 return " ~ name ~ "!T(x, y, context); 342 }"; 343 } 344 345 //-------------------------------- 346 // CONSTANTS 347 //-------------------------------- 348 349 unittest 350 { 351 writeln("------- constants --------"); 352 } 353 354 public immutable D M_PI(D) = D(roundStr( 355 "3.1415926535897932384626433832795028841971693993751" ~ 356 "0582097494459230781640628620899862803482534211707", 357 D.precision)); 358 359 public immutable D M_E(D) = D(roundStr( 360 "2.7182818284590452353602874713526624977572470936999" ~ 361 "5957496696762772407663035354759457138217852516643", 362 D.precision)); 363 364 public immutable D M_LN2(D) = D(roundStr( 365 "0.69314718055994530941723212145817656807550013436025" ~ 366 "52541206800094933936219696947156058633269964186887", 367 D.precision)); 368 369 public immutable D M_LN10(D) = D(roundStr( 370 "2.3025850929940456840179914546843642076011014886287" ~ 371 "7297603332790096757260967735248023599720508959820", 372 D.precision)); 373 374 public immutable D M_LOG2E(D) = D(roundStr( 375 "1.4426950408889634073599246810018921374266459541529" ~ 376 "8593413544940693110921918118507988552662289350634", 377 D.precision)); 378 379 public immutable D M_LOG2T(D) = D(roundStr( 380 "3.3219280948873623478703194294893901758648313930245" ~ 381 "8061205475639581593477660862521585013974335937016", 382 D.precision)); 383 384 public immutable D M_LOG10E(D) = D(roundStr( 385 "4.3429448190325182765112891891660508229439700580366" ~ 386 "6566114453783165864649208870774729224949338431748", 387 D.precision)); 388 389 public immutable D M_LOGT2(D) = D(roundStr( 390 "0.30102999566398119521373889472449302676818988146210" ~ 391 "8541310427461127108189274424509486927252118186172", 392 D.precision)); 393 394 public immutable D M_SQRT2(D) = D(roundStr( 395 "1.4142135623730950488016887242096980785696718753769" ~ 396 "4807317667973799073247846210703885038753432764157", 397 D.precision)); 398 399 unittest 400 { // constants 401 static struct S { TD x; TD expect; } 402 S[] s = 403 [ 404 { M_E!TD, "2.718281828459045" }, 405 { M_PI!TD, "3.141592653589793" }, 406 { M_LN2!TD, "0.6931471805599453" }, 407 { M_LN10!TD, "2.302585092994046" }, 408 { M_SQRT2!TD, "1.414213562373095" }, 409 // { TD.INV_PI,"0.318309886" }, 410 ]; 411 auto f = FunctionTest!(S,TD)("constants"); 412 foreach (t; s) f.test(t, t.x); 413 writefln(f.report); 414 } 415 416 mixin template MCInit(D, string str) 417 { 418 enum D typeValue = roundString(str, D.precision); 419 static D value = typeValue; 420 static int last = D.precision; 421 static D highValue = typeValue; 422 static int high = D.precision; 423 } 424 425 //mixin template MCType(D) 426 //{ 427 // if 428 429 /// If a constant has already been calculated, this function attempts 430 /// to use an existing value rather than recalculate. 431 /// If the constant has not yet been calculated to the desired precision, 432 /// returns false. 433 package D Constant(D, D typeValue)(/*D function(int) calc, */int precision) 434 if (isDecimal!D) 435 { 436 static D value = typeValue; 437 static int last = D.precision; 438 static D highValue = typeValue; 439 static int high = D.precision; 440 441 if (precision == D.precision) return typeValue; 442 443 // if the input precision <= last precision used 444 if (precision == last) return value; 445 if (precision < last) 446 { 447 last = precision; 448 value = round(value, precision); 449 return value; 450 } 451 452 // if the input precision <= highest precision used 453 if (precision == high) 454 { 455 last = high; 456 value = highValue; 457 return value; 458 } 459 if (precision < high) 460 { 461 last = precision; 462 value = round(highValue, precision); 463 return value; 464 } 465 high = precision; 466 last = precision; 467 return D.nan; 468 } 469 470 private Context calcContext(D)(int precision, int guardDigits = 2) 471 { 472 return Context(precision + guardDigits, D.maxExpo, HALF_EVEN); 473 } 474 475 /// Returns pi (3.14159265...) at the specified precision. 476 /// If the precision is less than or equal to a prior precision, 477 /// the earlier calculated value is returned (rounded if needed). 478 /// If the specified precision is higher than any previously calculated 479 /// precision, then the constant is recalculated and the higher value 480 /// is retained for subsequent use. 481 /// Repeated calls to the function at the same precision perform no rounding 482 /// or calculation. 483 public static D pi(D)(int precision = D.precision) if (isDecimal!D) 484 { 485 D value = Constant!(D, M_PI!D)(precision); 486 if (value.isNaN) value = calcPi!D(precision); 487 return value; 488 } 489 490 /// Returns pi (3.14159265...) for the specified context. 491 public D calcPi(D)(int precision) if (isDecimal!D) 492 { 493 // TODO: (behavior) if only 2 guard digits are used, function doesn't return 494 auto ctxt = calcContext!D(precision, 3); 495 // AGM algorithm 496 long k = 1; 497 D a0 = D.one; 498 D b0 = D.HALF * sqrt2!D(precision); 499 D s0 = D(5,-1);//.HALF; 500 D a1, b1, s1; 501 // loop until the arithmetic mean equals the geometric mean 502 while (!equals(a0, b0, ctxt)) 503 { 504 // arithmetic mean: a1 = (a0+bo)/2)) 505 a1 = mul(D.HALF, add(a0, b0, ctxt), ctxt); 506 // geometric mean: b1 = sqrt(a0*b0) 507 b1 = sqrt(mul(a0, b0, ctxt), ctxt); 508 k *= 2; 509 s1 = sub(s0, mul(sub(sqr(a1, ctxt), sqr(b1, ctxt), ctxt), k, ctxt), ctxt); 510 a0 = a1; 511 b0 = b1; 512 s0 = s1; 513 } 514 D pi = mul(div(sqr(a1, ctxt), s1, ctxt), 2, ctxt); 515 return round(pi, precision, D.mode); 516 } 517 518 unittest 519 { // pi 520 static struct S { int n; TD expect; } 521 S[] s = 522 [ 523 { 9, "3.14159265" }, 524 { 10, "3.141592654" }, 525 { 12, "3.14159265359" }, 526 { 20, "3.1415926535897932385" }, 527 { 14, "3.1415926535898" }, 528 { 16, "3.141592653589793" }, 529 { 18, "3.14159265358979324" }, 530 { 22, "3.141592653589793238463" }, 531 { 23, "3.1415926535897932384626" }, 532 { 24, "3.14159265358979323846264" }, 533 { 25, "3.141592653589793238462643" }, 534 { 26, "3.1415926535897932384626434" }, 535 ]; 536 auto f = FunctionTest!(S,TD)("pi"); 537 foreach (t; s) f.test(t, pi!TD(t.n), t.n); 538 writefln(f.report); 539 } 540 541 /*//mixin (Constant!("pi_2")); 542 package T pi_2(T)(Context inContext) if (isDecimal!T) 543 { 544 auto context = guard(inContext); 545 T halfPi = mul(pi!T(context), T.HALF, context); 546 return round(halfPi, inContext); 547 }*/ 548 549 /*unittest 550 { // pi_2 551 static struct S { int n; TD expect; } 552 S[] s = 553 [ 554 { 9, "1.57079633" }, 555 { 25, "1.570796326794896619231322" }, 556 { 5, "1.5708234" }, // note extra incorrect digits 557 ]; 558 auto f = FunctionTest!(S,TD)("pi/2"); 559 foreach (t; s) f.test(t, TD.pi_2(t.n), t.n); 560 writefln(f.report); 561 }*/ 562 563 //mixin (Constant!("M_1_PI")); 564 // TODO: shouldn't this be a calculation without a division? 565 /// Calculates the value of 1/pi in the specified context. 566 package D M_1_PI(D)(Context inContext) if (isDecimal!D) 567 { 568 auto context = guard(inContext, 4); 569 D alpha = div(D.ONE, pi!D(context), context); 570 return round(alpha, inContext); 571 } 572 573 /*unittest 574 { // M_1_PI 575 static struct S { int n; TD expect; } 576 S[] s = 577 [ 578 { 9, "0.318309886" }, 579 { 25, "0.3183098861837906715377675" }, 580 { 5, "0.3183144449" }, // note extra incorrect digits 581 ]; 582 auto f = FunctionTest!(S,TD)("1/pi"); 583 foreach (t; s) f.test(t, TD.M_1_PI(t.n), t.n); 584 writefln(f.report); 585 }*/ 586 587 //mixin (Constant!("M_2_PI")); 588 // TODO: (efficiency) Need to ensure that previous version of pi isn't reset. 589 // TODO: shouldn't this be a calculation without a division? 590 /// Calculates the value of 1/pi in the specified context. 591 package D M_2_PI(D)(Context inContext) if (isDecimal!D) 592 { 593 auto context = guard(inContext, 4); 594 D alpha = div(D.TWO, pi!D(context.precision), context); 595 return round(alpha, inContext); 596 } 597 598 /*unittest 599 { // M_2_PI 600 static struct S { int n; TD expect; } 601 S[] s = 602 [ 603 { 9, "0.636619772" }, 604 { 25, "0.6366197723675813430755351" }, 605 { 5, "0.63662" }, 606 ]; 607 auto f = FunctionTest!(S,TD)("2/pi"); 608 foreach (t; s) f.test(t, TD.M_2_PI(t.n), t.n); 609 writefln(f.report); 610 }*/ 611 612 613 public D e(D)(int precision = D.precision) if (isDecimal!D) 614 { 615 D value = Constant!(D, M_E!D)(precision); 616 if (value.isNaN) value = calcE!D(precision); 617 return value; 618 } 619 620 /// Calculates the value of e to the specified precision. 621 /// Returns the calculated value. 622 package D calcE(D)(int precision = D.precision) if (isDecimal!D) 623 { 624 auto context = calcContext!D(precision); 625 auto epsilon = D.epsilon(context); 626 // initialize Taylor series. 627 D term = D.one; 628 D sum = D.one; 629 // loop until the term is too small to affect the sum. 630 long n = 2; 631 while (term > epsilon) { 632 sum = add(sum, term, context); 633 term = div!D(term, n, context); 634 n++; 635 } 636 return round(sum, precision, D.mode); 637 } 638 639 unittest 640 { // e 641 static struct S { int n; TD expect; } 642 S[] s = 643 [ 644 { 9, "2.71828183" }, 645 { 25, "2.7182818284590452353602874713526625" }, 646 { 5, "2.7183" }, 647 ]; 648 auto f = FunctionTest!(S,TD)("e"); 649 foreach (t; s) f.test(t, e!TD(t.n), t.n); 650 writefln(f.report); 651 } 652 653 /// natural logarithm of 2 = 0.693147806... 654 public D ln2(D)(int precision = D.precision) 655 { 656 D value = Constant!(D, M_LN2!D)(precision); 657 if (value.isNaN) value = log(D.TEN, precision); 658 return value; 659 } 660 661 /// natural logarithm of 10 = 2.30258509... 662 public D ln10(D)(int precision = D.precision) 663 { 664 D value = Constant!(D, M_LN10!D)(precision); 665 if (value.isNaN) value = log(D.TEN, precision); 666 return value; 667 } 668 669 670 /// base 2 logarithm of e = 1.44269504... 671 public D lbe(D)(int precision = D.precision) 672 { 673 mixin MCInit!(D, 674 "1.44269504088896340735992468100189213" ~ 675 "742664595415298593413544940693110921918118507988552662289350634" ); 676 677 if (precision == D.precision) return typeValue; 678 679 // attempt to use previously calculated values 680 if (Constant!D(precision, last, value, high, highValue)) return value; 681 682 high = precision; 683 last = high; 684 value = log(D.TEN, precision); 685 return value; 686 } 687 688 689 /*/// base 2 logarithm of 10 = 3.32192809... 690 public enum decimal LB10 = roundString( 691 "3.3219280948873623478703194294893901" ~ 692 "7586483139302458061205475639581593477660862521585013974335937016", 693 decimal.precision); 694 695 /// base 10 logarithm of 2 = 0.301029996... 696 public enum decimal LG2 = roundString( 697 "0.3010299956639811952137388947244930" ~ 698 "26768189881462108541310427461127108189274424509486927252118186172", 699 decimal.precision); 700 701 /// base 10 logarithm of e = 4.34294482... 702 public enum decimal LGE = roundString( 703 "4.3429448190325182765112891891660508" ~ 704 "2294397005803666566114453783165864649208870774729224949338431748", 705 decimal.precision); 706 707 /// golden ratio = 1.6180339887... 708 public enum decimal PHI = roundString( 709 "1.6180339887498948482045868343656381177" ~ 710 "20309179805762862135448622705260462818902449707207204189391137", 711 decimal.precision); 712 */ 713 /// Returns the square root of two (1.41421357...) at the specified precision. 714 public D sqrt2(D)(int precision = D.precision) 715 { 716 D value = Constant!(D, M_SQRT2!D)(precision); 717 if (value.isNaN) value = sqrt(D(2), precision); 718 return value; 719 } 720 721 722 /*//mixin (Constant!("ln10")); 723 package enum T ln10(T)(Context context) if (isDecimal!T) 724 { 725 return log(T.TEN, context, false); 726 }*/ 727 728 //mixin (Constant!("ln2")); 729 package enum T ln2(T)(Context context) if (isDecimal!T) 730 { 731 return log(T.TWO, context, false); 732 } 733 734 //mixin (Constant!("log2_e")); 735 package enum T log2_e(T)(Context context) if (isDecimal!T) 736 { 737 return div(T.one, log(T.TWO, context, false), context); 738 } 739 740 //mixin (Constant!("log2_10")); 741 package enum T log2_10(T)(Context inContext) if (isDecimal!T) 742 { 743 auto context = guard(inContext); 744 T log2T = div(log(T.TEN, context, false), log(T.TWO, context, false), context); 745 return round(log2T, inContext); 746 // return round(TD("18690473486004564289165545643685440097"), inContext); 747 // return round(TD("18690473486004564245643685440097"), inContext); 748 } 749 750 //mixin (Constant!("log10_e")); 751 package enum T log10_e(T)(Context context) { 752 return T.one/log(T.TEN, context, false); 753 } 754 755 //mixin (Constant!("log10_2")); 756 package enum T log10_2(T)(Context context) { 757 return log(T.TWO, context)/log(T.TWO, context, false); 758 } 759 760 //mixin (Constant!("M_SQRT2")); 761 package enum T sqrt2(T)(Context context) if (isDecimal!T) 762 { 763 return sqrt(T.TWO, context); 764 } 765 766 //mixin (Constant!("M_SQRT1_2")); 767 package enum D M_SQRT1_2(D)(Context context) if (isDecimal!D) 768 { 769 return sqrt(D.HALF, context); 770 } 771 772 //mixin (Constant!("phi")); 773 package enum T phi(T)(Context context) if (isDecimal!T) 774 { 775 return mul(add(T(1) , sqrt(T(5), context), context), T.half, context); 776 } 777 778 //mixin (Constant!("invSqrtPi")); 779 package enum T invSqrtPi(T)(Context inContext) if (isDecimal!T) 780 { 781 auto context = guard(inContext, 4); 782 T alpha = div(T.one, sqrt(pi!T(context), context), context); 783 return round(alpha, inContext); 784 } 785 786 /*unittest 787 { // constants 788 static struct S { TD x; int p; TD expect; } 789 S[] s = 790 [ 791 { TD.ln10, 9, "2.30258509" }, 792 { TD.ln2, 9, "0.693147181" }, 793 { TD.log2_e, 9, "1.44269504" }, 794 { TD.log2_10, 9, "3.32192809" }, 795 { TD.M_SQRT2, 9, "1.41421356" }, 796 { TD.M_SQRT1_2, 9, "0.707106781" }, 797 { TD.phi, 9, "1.61803399" }, 798 { TD.ln10(16), 16, "2.302585092994046" }, 799 { TD.ln2(16), 16, "0.6931471805599453" }, 800 { TD.log2_e(16), 16, "1.442695040888963" }, 801 { TD.log2_10(16), 16, "3.21928094887362" }, // FIXTHIS: returns 3.321457567817785 802 { TD.M_SQRT2(16), 16, "1.414213562373095" }, 803 { TD.M_SQRT1_2(16), 16, "0.7071067811865475" }, 804 { TD.phi(16), 16, "1.618033988749895" }, 805 ]; 806 auto f = FunctionTest!(S,TD)("constants"); 807 foreach (t; s) f.test(t, t.x, t.p); 808 writefln(f.report); 809 }*/ 810 811 //-------------------------------- 812 // ALGEBRAIC FUNCTIONS 813 //-------------------------------- 814 815 /// Returns true if n is odd, false otherwise. 816 private bool isOdd(int n) { 817 return n & 1; 818 } 819 820 unittest { // isOdd 821 assertTrue(isOdd(3)); 822 assertTrue(!isOdd(8)); 823 assertTrue(isOdd(-1)); 824 } 825 826 // TODO: (behavior) add bitshift function? 827 828 //mixin (UnaryFunction!("reciprocal")); 829 mixin (UnaryFunction!("invSqrt")); 830 mixin (UnaryFunction!("sqrt")); 831 832 /// Returns the value of the function at the specified precision. 833 public T reciprocal(T, U:int)(T x, U precision) if (isDecimal!T) 834 { 835 if (x.isNaN) { 836 contextFlags.set(INVALID_OPERATION); 837 return T.nan; 838 } 839 Context context = Context(precision, T.maxExpo, HALF_EVEN); 840 return reciprocal!T(x, context); 841 } 842 843 /*/// Returns the value of the function at the specified precision. 844 public T reciprocal(T, U)(in U u, int precision) 845 if (isDecimal!T && isConvertible!U) 846 { 847 T x = T(u); 848 if (x.isNaN) { 849 contextFlags.set(INVALID_OPERATION); 850 return T.nan; 851 } 852 Context context = Context(precision, T.maxExpo, HALF_EVEN); 853 return reciprocal!T(T(x), context); 854 }*/ 855 856 // TODO: what happens if x is very close to zero or one? 857 // Does it try to work with the numbers anyway?? 858 public T reciprocal(T)(in T x, Context inContext = T.context) if (isDecimal!T) 859 { 860 // special values 861 if (x.isZero) { 862 contextFlags.set(DIVISION_BY_ZERO); 863 return T.infinity(x.sign); 864 } 865 if (x.copyAbs.isOne) return x.copy; 866 if (x.isInfinite) return T.zero(x.sign); 867 868 // extend working precision 869 auto context = guard(inContext); 870 871 // initial estimate 872 T a = x.reduce; 873 T r1 = T(2, -ilogb(a)-1); 874 875 // Newton's method 876 while (true) { 877 T r0 = r1; 878 r1 = mul(r0, sub(T.TWO, mul(a, r0, context), context), context); 879 if (equals(r0, r1 ,context)) break; 880 } 881 // round to the original precision 882 return round(r1, context); 883 } 884 885 unittest 886 { // reciprocal 887 static struct S { TD x; TD expect; } 888 S[] s = 889 [ 890 // { "1234567890123456789", TD(1)/TD("1234567890123456789") }, 891 // { "1234567890678900000", TD(1)/TD("1234567890123456789") }, 892 { "125", "0.008" }, 893 { "0.008", "125" }, 894 ]; 895 auto f = FunctionTest!(S,TD)("reciprocal"); 896 foreach (t; s) f.test(t, reciprocal(t.x)); 897 writefln(f.report); 898 } 899 900 // TODO: see note at reciprocal 901 public T invSqrt(T)(T x, Context inContext) if (isDecimal!T) 902 { 903 // special values 904 if (x.isZero) { 905 contextFlags.set(DIVISION_BY_ZERO); 906 return T.infinity(x.sign); 907 } 908 if (x.isOne) return x; 909 if (x.isInfinite) return T.zero(x.sign); 910 911 // increase the working precision 912 auto context = guard(inContext); 913 914 // initial estimate 915 T a; 916 int k = ilogb(x); 917 if (isOdd(k)) { 918 a = T(2, -1); 919 } 920 else { 921 a = T(5, -1); 922 k++; 923 } 924 const t = T(3); 925 // reduce the exponent 926 x.expo = x.expo - k - 1; 927 // Newton's method 928 while (true) { 929 T b = a; 930 a = mul(b, mul(T.HALF, sub(t, mul(x, sqr(b, context), context), context), context), context); 931 if (equals(b, a, context)) break; 932 } 933 // restore the exponent 934 a.expo = a.expo - k/2 - 1; 935 // round to the original precision 936 return round(a, inContext); 937 } 938 939 /*unittest 940 { // invSqrt 941 static struct S { TD x; int n; TD expect; } 942 S[] s = 943 [ 944 { "2", 6, "0.707107" }, 945 { "20", 9, "0.223606798" }, 946 { "300", 9, "0.0577350269" }, 947 { "98763", 9, "0.00318201969" }, 948 { "98763098", 9, "0.000100624248" }, 949 { "0.008", 9, "1.11803399" }, 950 { "98763098", 9, "0.000100624248" }, 951 { "9876387982347", 9, "3.18200552E-7" }, 952 { "2", 14, "0.70710678118655" }, 953 { "4000", 11, "0.015811388301" }, 954 ]; 955 auto f = FunctionTest!(S,TD)("invSqt"); 956 foreach (t; s) f.test(t, invSqrt(t.x,t.n)); 957 writefln(f.report); 958 }*/ 959 960 /// Returns the square root of the argument to the current precision. 961 /// Uses Newton's method. 962 public D sqrt(D)(D arg, Context context) if (isDecimal!D) 963 { 964 // auto context = guard(inContext, 3); 965 // special values 966 if (arg.isNegative) { 967 contextFlags.set(INVALID_OPERATION); 968 return D.nan; 969 } 970 // TODO: what if arg is very close to one or zero?? 971 if (arg.isOne) return D.one; 972 if (arg.isZero) return D.zero; 973 if (arg.isInfinite) return D.infinity; 974 975 // reduce the exponent and estimate the result 976 D value; 977 int k = ilogb(arg); 978 if (isOdd(k)) { 979 value = D(6, -1); 980 } 981 else { 982 value = D(2, -1); 983 k++; 984 } 985 arg.expo = arg.expo - k - 1; 986 987 // Newton's method 988 while (true) { 989 D prev = value; 990 value = mul(D.HALF, add(prev, div(arg, prev, context), context), context); 991 if (equals(value, prev, context)) break; 992 } 993 // restore the exponent 994 value.expo = value.expo + (k+1)/2; 995 // round the result 996 return round(value, context); 997 } 998 999 unittest 1000 { // sqrt 1001 static struct S { TD x; int p; TD expect; } 1002 S[] s = 1003 [ 1004 { "2", 9, "1.41421356" }, 1005 { "0.707106781187", 12, "0.840896415254" }, 1006 { "200", 21, "14.142135623730950488000" }, 1007 { "25", 8, "5.0" }, 1008 { "2E-5", 10, "0.00447213595500" }, 1009 { "1E-15", 9, "3.16227766E-8" }, 1010 { "1E-16", 9, "1.00000000E-8" }, 1011 ]; 1012 auto f = FunctionTest!(S,TD)("sqrt"); 1013 foreach (t; s) f.test(t, sqrt(t.x, t.p), t.p); 1014 writefln(f.report); 1015 } 1016 1017 //-------------------------------- 1018 // EXPONENTIAL AND LOGARITHMIC FUNCTIONS 1019 //-------------------------------- 1020 1021 /+mixin (UnaryFunction!("exp")); 1022 1023 "/// Returns the value of the function at the specified precision. 1024 public T " ~ name ~ "(T)(T x, int precision = T.precision) if (isDecimal!T) 1025 { 1026 if (x.isNaN) { 1027 contextFlags.set(INVALID_OPERATION); 1028 return T.nan; 1029 } 1030 Context context = Context(precision, T.maxExpo, HALF_EVEN); 1031 return " ~ name ~ "!T(x, context); 1032 } 1033 +/ 1034 1035 //-------------------------------- 1036 // exponentials and logarithms 1037 //-------------------------------- 1038 1039 /// Adds guard digits to the current precision and sets rounding to HALF_EVEN. 1040 /// This is useful for extended calculation to minimize errors. 1041 /// Returns a new context with the precision increased by the guard digits value. 1042 /// Note that this does not create a new Decimal type with this context. 1043 public Context guard(Context context, int guardDigits = 2) 1044 { 1045 return Context(context.precision + guardDigits, context.maxExpo, HALF_EVEN); 1046 } 1047 1048 /// Changes the current precision and sets rounding to HALF_EVEN. 1049 /// This is useful for extended calculation to minimize errors. 1050 /// Returns a new context with the precision increased by the guard digits value. 1051 /// Note that this does not create a new Decimal type with this context. 1052 public Context changePrecision(Context context, int precision) 1053 { 1054 return Context(precision, context.maxExpo, HALF_EVEN); 1055 } 1056 1057 /// Returns the exponent of the argument at the specified precision. 1058 public D exp(D)(in D arg, int precision) 1059 if (isDecimal!D || isConvertible(D)) 1060 { 1061 D num = D(arg); 1062 1063 // check for nan 1064 if (num.isNaN) 1065 { 1066 contextFlags.set(INVALID_OPERATION); 1067 return D.nan; 1068 } 1069 // create new context at given precision 1070 Context context = changePrecision(D.context, precision); 1071 // call the function 1072 return exp!D(num, context); 1073 } 1074 1075 /// Returns the exponent of the argument at the current precision. 1076 /// Decimal version of std.math function. 1077 /// Required by General Decimal Arithmetic Specification 1078 public D exp(D)(D num, Context context = D.context, int guardDigits = 2) 1079 if (isDecimal!D) 1080 { 1081 if (num.isInfinite) 1082 { 1083 return num.isNegative ? D.zero : num; 1084 } 1085 bool negative = num.isNegative; 1086 if (negative) num = num.copyAbs; 1087 auto guarded = guard(context, guardDigits); 1088 D sqrx = sqr(num, guarded); 1089 long n = 1; 1090 D fact = 1; 1091 D t1 = D.one; 1092 D t2 = num; 1093 D term = add(t1, t2, guarded); 1094 D sum = term; 1095 while (term > D.epsilon(guarded)) 1096 { 1097 n += 2; 1098 t1 = mul(t2, mul(num, n, guarded), guarded); 1099 t2 = mul(t2, sqrx, guarded); 1100 fact = mul(fact, n*(n-1), guarded); 1101 term = div(add(t1, t2, guarded), fact, guarded); 1102 sum = add(sum, term, guarded); 1103 } 1104 if (negative) sum = div(D.one, sum, guarded); 1105 return round(sum, context); 1106 } 1107 1108 unittest 1109 { // exp 1110 static struct S { TD x; int p; TD expect; } 1111 S[] s = 1112 [ 1113 { 1, 9, "2.71828183" }, 1114 { 2, 9, "7.3890560989306502272" }, 1115 { -2, 9, "0.13533528324" }, 1116 { 1, 9, "2.71828183" }, 1117 { 1, 11, "2.7182818285" }, 1118 { 1, 15, "2.71828182845905" }, 1119 { 2, 15, "7.3890560989306502272" }, 1120 { 2, 11, "7.3890560989306502272" }, 1121 { -2, 11, "0.13533528324" }, 1122 ]; 1123 auto f = FunctionTest!(S,TD)("exp"); 1124 foreach (t; s) f.test(t, exp(t.x, t.p), t.p); 1125 writefln(f.report); 1126 } 1127 1128 public enum D ln10(D)(Context context) if (isDecimal!D) 1129 { 1130 return log(D.TEN, context, false); 1131 } 1132 1133 /// Returns the logarithm of the argument at the specified precision. 1134 public D log(D)(in D arg, int precision = D.precision) 1135 if (isDecimal!D || isConvertible(D)) 1136 { 1137 D num = D(arg); 1138 // check for nan 1139 if (num.isNaN) 1140 { 1141 contextFlags.set(INVALID_OPERATION); 1142 return D.nan; 1143 } 1144 // create new context at input precision 1145 Context context = Context(precision, D.maxExpo, HALF_EVEN); 1146 // call the function 1147 return log!D(num, context); 1148 } 1149 1150 public D log(D)(D num, Context context, 1151 bool reduceArg = true) if (isDecimal!D) 1152 { 1153 if (num.isZero) 1154 { 1155 contextFlags.set(DIVISION_BY_ZERO); 1156 return -D.infinity; 1157 } 1158 if (num.isNegative) return D.nan; 1159 if (num.isInfinite) return D.infinity; 1160 1161 auto guarded = guard(context); 1162 int k; 1163 if (reduceArg) 1164 { 1165 k = ilogb(num) + 1; 1166 num.expo = num.expo - k; 1167 } 1168 D a = div(sub(num, 1, guarded), add(num, 1, guarded), guarded); 1169 D b = sqr(a, guarded); 1170 D c = a; 1171 long n = 3; 1172 while (true) 1173 { 1174 c = mul(c, b, guarded); 1175 D d = add(a, div(c, n, guarded), guarded); 1176 if (equals(a, d, guarded)) 1177 { 1178 D ln = mul(a, 2, guarded); 1179 if (reduceArg) 1180 { 1181 ln = add(ln, mul(ln10!D(guarded), k, guarded), guarded); 1182 } 1183 return round(ln, context); 1184 } 1185 a = d; 1186 n += 2; 1187 } 1188 } 1189 1190 unittest 1191 { // log 1192 static struct S { TD x; int p; TD expect; } 1193 S[] s = 1194 [ 1195 { "2.71828183", 9, "1.0" }, 1196 { "10.00", 12, "2.30258509299" }, 1197 { "10.00", 9, "2.30258509" }, 1198 { "123.45", 9, "4.81583622" }, 1199 { "99.999E+8", 9, "23.0258409" }, 1200 { "99.999E+8", 7, "23.0258409" }, 1201 { "99.999E+8", 15, "23.0258409298905" }, 1202 { "99.999E+8", 9, "23.0258409" }, 1203 ]; 1204 auto f = FunctionTest!(S,TD)("log"); 1205 foreach (t; s) f.test(t, log(t.x, t.p), t.p); 1206 writefln(f.report); 1207 } 1208 1209 1210 /*/// Decimal version of std.math function. 1211 /// Required by General Decimal Arithmetic Specification 1212 package D exp(D)(D x, Context inContext) if (isDecimal!D) 1213 { 1214 if (x.isInfinite) 1215 { 1216 return x.isNegative ? D.zero : x; 1217 } 1218 bool negative = x.isNegative; 1219 if (negative) x = x.copyAbs; 1220 auto context = guard(inContext); 1221 D sqrx = sqr(x, context); 1222 long n = 1; 1223 D fact = 1; 1224 D t1 = D.one; 1225 D t2 = x; 1226 D term = add(t1, t2, context); 1227 D sum = term; 1228 while (term > D.epsilon(context)) 1229 { 1230 n += 2; 1231 t1 = mul(t2, mul(x, n, context), context); 1232 t2 = mul(t2, sqrx, context); 1233 fact = mul(fact, n*(n-1), context); 1234 term = div(add(t1, t2, context), fact, context); 1235 sum = add(sum, term, context); 1236 } 1237 if (negative) sum = div(D.one, sum, context); 1238 return round(sum, inContext); 1239 } 1240 1241 unittest 1242 { // exp 1243 static struct S { TD x; int p; TD expect; } 1244 S[] s = 1245 [ 1246 { 1, 9, "2.71828183" }, 1247 { 2, 9, "7.3890560989306502272" }, 1248 { -2, 9, "0.13533528324" }, 1249 { 1, 9, "2.71828183" }, 1250 { 1, 11, "2.7182818285" }, 1251 { 1, 15, "2.71828182845905" }, 1252 { 2, 15, "7.3890560989306502272" }, 1253 { 2, 11, "7.3890560989306502272" }, 1254 { -2, 11, "0.13533528324" }, 1255 ]; 1256 auto f = FunctionTest!(S,TD)("exp"); 1257 foreach (t; s) f.test(t, exp(t.x, t.p), t.p); 1258 writefln(f.report); 1259 } 1260 */ 1261 /+ 1262 /** 1263 * Decimal version of std.math function. 1264 * 2^x 1265 */ 1266 public Decimal exp2T)( arg) { 1267 Decimal result; 1268 return result; 1269 } 1270 1271 unittest { 1272 write("exp2..........."); 1273 writeln("test missing"); 1274 } 1275 +/ 1276 1277 mixin (UnaryFunction!("expm1")); 1278 1279 /// expm1(x) will be more accurate than exp(x) - 1 for x << 1. 1280 /// Decimal version of std.math function. 1281 /// Reference: Beebe, Nelson H. F., "Computation of expm1(x) = exp(x) - 1". 1282 public T expm1(T)(T x, Context inContext) if (isDecimal!T) 1283 { 1284 // special values 1285 if (x.isZero) return x; 1286 if (x.isInfinite) return x.isNegative ? -T.one : T.infinity; 1287 1288 auto context = guard(inContext); 1289 T sum = T.zero; 1290 1291 // if too large return exp(x) - 1. 1292 const T lower = T("-0.7"); 1293 const T upper = T("0.5"); 1294 // ??? what is this --> if (x.copyAbs < lower || x.copyAbs > upper) { 1295 if (x < lower || x > upper) { 1296 sum = sub(exp(x, context), 1, context); 1297 return round(sum, inContext); 1298 } 1299 1300 bool negative = x.isNegative; 1301 if (negative) x = x.copyAbs; 1302 /* // if too large return exp(x) - 1. 1303 if (x < lower || x > upper) { 1304 sum = sub(exp(x, context), 1, context); 1305 return round(sum, inContext); 1306 }*/ 1307 1308 // otherwise return expm1(x) 1309 T term = x; 1310 long n = 1; 1311 while (term.copyAbs > T.epsilon(context)) { 1312 sum = add(sum, term, context); 1313 n++; 1314 term = mul(term, div(x, n, context), context); 1315 } 1316 if (negative) sum = div(T.one, sum, context); 1317 return round(sum, inContext); 1318 } 1319 1320 unittest 1321 { // expm1 1322 static struct S { TD x; int p; TD expect; } 1323 S[] s = 1324 [ 1325 { "0.1", 9, "0.105170918" }, 1326 // FIXTHIS: incorrect result for negative numbers. 1327 { "-0.4", 9, "-0.329679954" }, 1328 { "-2", 9, "-0.864664717" }, 1329 ]; 1330 auto f = FunctionTest!(S,TD)("expm1"); 1331 foreach (t; s) f.test(t, expm1(t.x, t.p), t.p); 1332 writefln(f.report); 1333 } 1334 1335 //mixin (UnaryFunction!("log")); 1336 mixin (UnaryFunction!("log1p")); 1337 mixin (UnaryFunction!("log10")); 1338 mixin (UnaryFunction!("log2")); 1339 1340 /+ 1341 "/// Returns the value of the function at the specified precision. 1342 public T " ~ name ~ "(T)(T x, int precision = T.precision) if (isDecimal!T) 1343 { 1344 if (x.isNaN) { 1345 contextFlags.set(INVALID_OPERATION); 1346 return T.nan; 1347 } 1348 Context context = Context(precision, T.maxExpo, HALF_EVEN); 1349 return " ~ name ~ "!T(x, context); 1350 } 1351 +/ 1352 1353 // TODO: (behavior) add log(number, base) function (will have to have a different name -- logBase or something 1354 /// Decimal version of std.math function. 1355 /// Required by General Decimal Arithmetic Specification 1356 // TODO: efficiency) see Natural Logarithm, Wikipedia. 1357 /** 1358 * log1p (== log(1 + x)). 1359 * Decimal version of std.math function. 1360 */ 1361 public T log1p(T)(T x, Context inContext) if (isDecimal!T) 1362 { 1363 // special cases 1364 if (x.isNaN || x < T.NEG_ONE) // use compare(x, T.NEG_ONE) == -1? 1365 { 1366 contextFlags.set(INVALID_OPERATION); 1367 return T.nan; 1368 } 1369 // check for infinite argument 1370 if (x.isInfinite) return T.infinity; 1371 1372 if (x.isZero) return T.zero(x.sign); 1373 1374 if (equals(x, T.NEG_ONE, inContext)) return T.infinity(true); 1375 1376 // TODO: There's probably a better breakeven point 1377 if (x.copyAbs >= T.one) return log(add(T.one, x, inContext), inContext); 1378 1379 T term = x; 1380 T pwr = x; 1381 T sum = T.zero; 1382 T n = T.one; 1383 auto context = guard(inContext); 1384 while (term.copyAbs >= T.epsilon(context)) 1385 { 1386 sum = add(term, sum, context); 1387 pwr = mul(-pwr, x, context);// * x; 1388 n++; 1389 term = div(pwr, n, context); 1390 } 1391 sum = add(term, sum, context); 1392 //if (!__ctfe) writeln; 1393 //if (!__ctfe) writefln("sum = %s", abstractForm(sum)); 1394 //if (!__ctfe) writefln("sum.digits = %s", sum.digits); 1395 //if (!__ctfe) writefln("inContext.precision = %s", inContext.precision); 1396 1397 sum = round(sum, inContext); 1398 //if (!__ctfe) writefln("sum = %s", abstractForm(sum)); 1399 //if (!__ctfe) writefln("sum.digits = %s", sum.digits); 1400 return sum; 1401 // return round(sum, inContext); 1402 } 1403 1404 // TODO: (testing) unittest this. 1405 unittest { 1406 write("-- log1p............"); 1407 TD x = "0.1"; 1408 assertEqual(log1p(x), "0.09531017980432486"); 1409 writeln("passed"); 1410 } 1411 1412 /// Decimal version of std.math.log10. 1413 /// Required by General Decimal Arithmetic Specification 1414 public T log10(T)(T x, Context inContext) if (isDecimal!T) 1415 { 1416 if (x.isZero) { 1417 contextFlags.set(DIVISION_BY_ZERO); 1418 return T.infinity; 1419 } 1420 if (x.isNegative) { 1421 return T.nan; 1422 } 1423 auto context = guard(inContext); 1424 int k = ilogb(x) + 1; 1425 x.expo = x.expo - k; 1426 // x.expo -= k; 1427 T lg10 = add(div(log(x, context), ln10!T(context)), k); 1428 return round(lg10, inContext); 1429 } 1430 1431 unittest { 1432 write("-- log10............"); 1433 TD x = TD("2.55"); 1434 assertEqual(log10(x), TD("0.4065401804339552")); 1435 x = 123.456; 1436 assertEqual(log10(x), TD("2.091512201627772")); 1437 x = 10.0; 1438 assertEqual(log10(x), 1); 1439 writeln("passed"); 1440 } 1441 1442 /** 1443 * Decimal version of std.math.log2. 1444 * Required by General Decimal Arithmetic Specification 1445 */ 1446 public T log2(T)(T x, Context inContext) if (isDecimal!T) 1447 { 1448 auto context = guard(inContext); 1449 T lg2 = div(log(x, context), ln2!T(context), context); 1450 return round(lg2, inContext); 1451 } 1452 1453 unittest { 1454 write("-- log2............."); 1455 assertEqual(log2(TD(10)), TD("3.321928094887362")); 1456 assertEqual(log2(e!TD), TD("1.442695040888963")); 1457 writeln("passed"); 1458 } 1459 1460 /** 1461 * Decimal version of std.math.pow. 1462 * Required by General Decimal Arithmetic Specification 1463 */ 1464 // TODO: (behavior) add context 1465 public T pow(T)(T x, T y) if (isDecimal!T) 1466 { 1467 return exp(x*log(y)); 1468 } 1469 1470 unittest { 1471 write("pow.........."); 1472 writeln("test missing"); 1473 } 1474 1475 mixin (BinaryFunction!("hypot")); 1476 1477 /// Returns the square root of the sum of the squares in the specified context. 1478 /// Decimal version of std.math.hypot. 1479 public T hypot(T)(T x, T y, Context context) if (isDecimal!T) 1480 { 1481 // special values 1482 if (x.isInfinite || y.isInfinite) return T.infinity(); 1483 if (x.isZero) return y; 1484 if (y.isZero) return x; 1485 if (x.isNaN || y.isNaN) { 1486 contextFlags.set(INVALID_OPERATION); 1487 return T.nan; 1488 } 1489 1490 T a = x.copyAbs; 1491 T b = y.copyAbs; 1492 if (a < b) { 1493 //swap operands 1494 T t = a; 1495 a = b; 1496 b = t; 1497 } 1498 b = div(b, a, context); 1499 return mul(a, sqrt(add(T.one, sqr(b, context), context), context)); 1500 } 1501 1502 // TODO: (testing) Need to test operation near precisions where this operation is really useful. 1503 unittest 1504 { // hypot 1505 static struct S { TD x; TD y; TD expect; } 1506 S[] s = 1507 [ 1508 { "3", "4", "5" }, 1509 ]; 1510 auto f = FunctionTest!(S,TD)("hypot"); 1511 foreach (t; s) f.test(t, hypot(t.x, t.y)); 1512 writefln(f.report); 1513 } 1514 1515 //-------------------------------- 1516 // TRIGONOMETRIC FUNCTIONS 1517 //-------------------------------- 1518 1519 //mixin (UnaryFunction!("sin")); 1520 //mixin (UnaryFunction!("cos")); 1521 1522 1523 // Returns the reduced argument and the quadrant. 1524 // Reduced argument |x| <= pi/4. 1525 private T reduceAngle(T)(in T x, 1526 out int n, in Context inContext = T.context) if (isDecimal!T) 1527 { 1528 auto context = guard(inContext); 1529 T M_2_PI = M_2_PI!T(context); 1530 T pi_2 = mul(pi!T(context.precision), T.HALF); 1531 T y = mul(x, M_2_PI, context); 1532 int k = rint(y).toInt; 1533 n = k % 4; 1534 T f = sub(y, k, context); 1535 T r = mul(f, pi_2, context); 1536 return r; 1537 } 1538 1539 /// Decimal version of std.math function. 1540 public T sin(T)(in T x, int precision = T.precision) if (isDecimal!T) 1541 { 1542 if (x.isNaN) { 1543 contextFlags.set(INVALID_OPERATION); 1544 return T.nan; 1545 } 1546 1547 auto context = Context(precision, T.maxExpo, HALF_EVEN); 1548 int k; 1549 T red = reduceAngle(x, k, context); 1550 switch (k) { 1551 case 0: return( sin( red, context)); 1552 case 1: return( cos( red, context)); 1553 case 2: return(-sin( red, context)); 1554 case 3: return(-cos( red, context)); 1555 default: return T.nan; 1556 } 1557 // return T.nan; 1558 } 1559 1560 // Decimal version of std.math function. 1561 // Precondition: x is in 1st quadrant. 1562 package T sin(T)(in T x, Context inContext) if (isDecimal!T) 1563 { 1564 auto context = guard(inContext); 1565 T sum = 0; 1566 int n = 1; 1567 T powx = x.copy; 1568 T sqrx = sqr(x, context); 1569 T fact = 1; 1570 T term = powx; 1571 while (term.copyAbs > T.epsilon(context)) { 1572 sum = add(sum, term, context); 1573 n += 2; 1574 powx = mul(powx.copyNegate, sqrx, context); 1575 fact = mul(fact, n*(n-1), context); 1576 term = div(powx, fact, context); 1577 } 1578 return round(sum, inContext); 1579 } 1580 1581 unittest 1582 { // sin 1583 static struct S { TD x; int p; TD expect; } 1584 S[] s = 1585 [ 1586 { "0.1", 9, "0.0998334166" }, 1587 { "0.1", 2, "0.10" }, 1588 { "0.333", 9, "0.326879693" }, 1589 { "5.0", 9, "-0.958924275" }, 1590 { "1.0", 9, "0.8414709848" }, 1591 { "1.0", 16, "0.8414709848078965" }, 1592 { "2.0", 16, "0.9092974268256817" }, 1593 ]; 1594 auto f = FunctionTest!(S,TD)("sin"); 1595 foreach (t; s) f.test(t, sin(t.x, t.p), t.p); 1596 writefln(f.report); 1597 } 1598 1599 unittest { 1600 write("-- sin.............."); 1601 // TD difficult = TD(5678900000); 1602 TD difficult = TD(5); 1603 writefln("difficult = %s", difficult); 1604 // FIXTHIS: throws div by zero exception... 1605 writefln("sin(difficult) = %s", sin(difficult)); 1606 // assertEqual(sin(difficult), TD(0)); 1607 // TODO: (testing) one value from each quadrant, reduced value. 1608 // TODO: (behavior) this is a notoriously difficult value "sin(10^^22)" 1609 writeln("passed"); 1610 } 1611 1612 /// Decimal version of std.math function. 1613 public T cos(T)(in T x, int precision = T.precision) if (isDecimal!T) 1614 { 1615 if (x.isNaN) { 1616 contextFlags.set(INVALID_OPERATION); 1617 return T.nan; 1618 } 1619 auto context = Context(precision, T.maxExpo, HALF_EVEN); 1620 int quadrant; 1621 T red = reduceAngle(x, quadrant, context); 1622 switch (quadrant) { 1623 case 0: return( cos(red, context)); 1624 case 1: return(-sin(red, context)); 1625 case 2: return(-cos(red, context)); 1626 case 3: return( sin(red, context)); 1627 default: return T.nan; 1628 } 1629 } 1630 1631 /// Decimal version of std.math function. 1632 /// Precondition: x is in 1st quadrant. 1633 package T cos(T)(in T x, Context inContext) { 1634 auto context = guard(inContext); 1635 T sum = 0; 1636 int n = 0; 1637 T powx = 1; 1638 T sqrx = sqr(x, context); 1639 T fact = 1; 1640 T term = powx; 1641 while (term.copyAbs > T.epsilon(context)) { 1642 sum = add(sum, term, context); 1643 n += 2; 1644 powx = mul(powx.copyNegate, sqrx, context); 1645 fact = mul(fact, n*(n-1), context); 1646 term = div(powx, fact, context); 1647 } 1648 return round(sum, inContext); 1649 } 1650 1651 unittest 1652 { // cos 1653 static struct S { TD x; int p; TD expect; } 1654 S[] s = 1655 [ 1656 { "1.0", 9, "0.5403023058681397174009" }, 1657 { "0.333", 9, "0.945065959" }, 1658 { "5.0", 9, "0.283662185" }, 1659 { "1.0", 23, "0.540302306" }, 1660 { "2.0", 16, "-0.416146837" }, 1661 ]; 1662 auto f = FunctionTest!(S,TD)("cos"); 1663 foreach (t; s) f.test(t, cos(t.x, t.p), t.p); 1664 writefln(f.report); 1665 } 1666 1667 public void sincos(T)(T x, out T sine, out T cosine, int precision = T.precision) { 1668 auto context = Context(precision, T.maxExpo, HALF_EVEN); 1669 int quadrant; 1670 T red = reduceAngle(x, quadrant, context); 1671 sincos(red, sine, cosine, context); 1672 /* switch (quadrant) { 1673 //sin: 1674 case 0: break; 1675 case 1: 1676 sine = cosine; 1677 cosine = -sine; 1678 break; 1679 case 2: 1680 sine = -sine; 1681 cosine = -cosine; 1682 break; 1683 case 3: 1684 sine = -cosine; 1685 cosine = sine; 1686 break; 1687 default: 1688 sine = T.nan; 1689 cosine = T.nan; 1690 }*/ 1691 } 1692 /** 1693 * Replaces std.math function expi 1694 * 1695 */ 1696 // TODO: (behavior) context, angle reduction 1697 public void sincos(T)(const T x, out T sine, out T cosine, 1698 Context inContext) if (isDecimal!T) 1699 { 1700 auto context = guard(inContext); 1701 T csum, cterm, cx; 1702 T ssum, sterm, sx; 1703 T sqrx = sqr(x, context); 1704 long n = 2; 1705 T fact = 1; 1706 cx = 1; cterm = cx; csum = cterm; 1707 sx = x; sterm = sx; ssum = sterm; 1708 while (sterm.copyAbs > T.epsilon) { 1709 cx = mul(cx.copyNegate, sqrx, context); 1710 fact = mul(fact, n++, context); 1711 cterm = div(cx, fact, context); 1712 csum = add(csum, cterm, context); 1713 sx = mul(sx.copyNegate, sqrx, context); 1714 fact = mul(fact, n++, context); 1715 sterm = div(sx, fact, context); 1716 ssum = add(ssum, sterm, context); 1717 } 1718 sine = round(ssum, inContext); 1719 cosine = round(csum, inContext); 1720 } 1721 1722 unittest { 1723 write("sincos......."); 1724 TD sine; 1725 TD cosine; 1726 sincos(TD("1.0"), sine, cosine); 1727 if (!__ctfe) writefln("sine = %s", sine); 1728 if (!__ctfe) writefln("cosine = %s", cosine); 1729 writeln("..failed"); 1730 } 1731 1732 /** 1733 * Decimal version of std.math function. 1734 * 1735 */ 1736 // TODO: (efficiency) compare divTan with tan. 1737 public T divTan(T)(T x) if (isDecimal!T) 1738 { 1739 T sine; 1740 T cosine; 1741 sincos(x, sine, cosine); 1742 if (sine == T.zero) return T.infinity; 1743 return sine/cosine; 1744 } 1745 1746 public T tan(T)(T x, int precision = T.precision) if (isDecimal!T) 1747 { 1748 if (x.isNaN) { 1749 contextFlags.set(INVALID_OPERATION); 1750 return T.nan; 1751 } 1752 auto context = Context(precision, T.maxExpo, HALF_EVEN); 1753 int quadrant; 1754 T red = reduceAngle(x, quadrant, context); 1755 T sine; 1756 T cosine; 1757 sincos(red, sine, cosine, context); 1758 switch (quadrant) { 1759 case 0: return( sine/cosine); 1760 case 1: return(-cosine/sine); 1761 case 2: return( sine/cosine); 1762 case 3: return(-sine/cosine); 1763 default: return T.nan; 1764 } 1765 } 1766 1767 unittest 1768 { // tan 1769 static struct S { TD x; int p; TD expect; } 1770 S[] s = 1771 [ 1772 { "1.0", 9, "1.55740772465490223" }, 1773 { "1.0", 14, "1.55740772465490223" }, 1774 { "0.333", 9, "0.345880295" }, 1775 ]; 1776 auto f = FunctionTest!(S,TD)("tan"); 1777 foreach (t; s) f.test(t, tan(t.x, t.p), t.p); 1778 writefln(f.report); 1779 } 1780 1781 mixin (UnaryFunction!("atan")); 1782 mixin (UnaryFunction!("asin")); 1783 mixin (UnaryFunction!("acos")); 1784 1785 /// Returns the arctangent of the argument in the specified context. 1786 /// Algorithm uses Taylor's theorem for arctangent. 1787 public T atan(T)(T x, Context inContext) if (isDecimal!T) 1788 { 1789 auto context = guard(inContext, 3); 1790 long k = 1; 1791 // reduce the input angle 1792 while (x > T(0.5)) { 1793 T a = sqr(x, context); 1794 T b = add(a, 1, context); 1795 T c = sqrt(b, context); 1796 T d = add(c, 1, context); 1797 x = div(x, d, context); 1798 k *= 2; 1799 } 1800 T sum = 0; 1801 T powx = x; 1802 T sqrx = sqr(x, context); 1803 long dvsr = 1; 1804 T term = powx; 1805 while (term.copyAbs > T.epsilon(context)) { 1806 sum = add(sum, term, context); 1807 powx = mul(powx.copyNegate, sqrx, context); 1808 dvsr = dvsr + 2; 1809 term = div!T(powx, dvsr, context); 1810 } 1811 return round(mul(sum, k, context), inContext); 1812 } 1813 1814 1815 unittest 1816 { // atan 1817 static struct S { TD x; TD expect; } 1818 S[] s = 1819 [ 1820 // { "1.0", "0.785398163397" }, 1821 // { "0.5", "0.463647609" }, 1822 // { "0.333", "0.32145052439664" }, 1823 // { "0.1", "0.099668652491162038065120043484057532623410224914551" }, 1824 // { "0.9", "0.73281510178650655085164089541649445891380310058594" }, 1825 ]; 1826 auto f = FunctionTest!(S,TD)("atan"); 1827 foreach (t; s) f.test(t, atan(t.x)); 1828 writefln(f.report); 1829 } 1830 1831 /// Decimal version of std.math function. 1832 // TODO: (behavior) convert to std unary function. 1833 public T asin(T)(T x) if (isDecimal!T) 1834 { 1835 T result = 2 * atan!T(x/(1+sqrt(1-sqr(x)))); //^^2))); 1836 return result; 1837 } 1838 1839 unittest 1840 { // asin 1841 static struct S { TD x; TD expect; } 1842 S[] s = 1843 [ 1844 { "1.0", "1.570796326794897" }, 1845 { "0.5", "0.5235987755982990" }, 1846 { "0.333", "0.3394833781504900" }, 1847 ]; 1848 auto f = FunctionTest!(S,TD)("asin"); 1849 foreach (t; s) f.test(t, asin!TD(t.x)); 1850 writefln(f.report); 1851 } 1852 1853 /// Decimal version of std.math function. 1854 // TODO: (behavior) convert to std unary function. 1855 public T acos(T)(T x) if (isDecimal!T) 1856 { 1857 T result = 2 * atan!T(sqrt(1-sqr(x))/(1 + x)); 1858 return result; 1859 } 1860 1861 unittest 1862 { // acos 1863 static struct S { TD x; TD expect; } 1864 S[] s = 1865 [ 1866 { "1.0", "0" }, 1867 // { "0.5", "1.04719755120" }, 1868 // { "0.333", "1.23131294864" }, 1869 ]; 1870 auto f = FunctionTest!(S,TD)("acos"); 1871 foreach (t; s) f.test(t, acos(t.x)); 1872 writefln(f.report); 1873 } 1874 1875 /// Decimal version of std.math function. 1876 public TD atan2(T)(T y, TD x) if (isDecimal!T) 1877 { 1878 TD result; 1879 return result; 1880 } 1881 1882 unittest { 1883 write("atan2........"); 1884 writeln("..failed"); 1885 } 1886 1887 //-------------------------------- 1888 // HYPERBOLIC TRIGONOMETRIC FUNCTIONS 1889 //-------------------------------- 1890 1891 mixin (UnaryFunction!("sinh")); 1892 mixin (UnaryFunction!("cosh")); 1893 mixin (UnaryFunction!("tanh")); 1894 //mixin (UnaryFunction!("atanh")); 1895 1896 /// Decimal version of std.math function. 1897 public T sinh(T)(T x, Context inContext) if (isDecimal!T) 1898 { 1899 auto context = guard(inContext); 1900 long n = 1; 1901 T sum = 0; 1902 T powx = x; 1903 T sqrx = sqr(x, context); 1904 T fact = n; 1905 T term = powx; 1906 while (term.copyAbs > T.epsilon(context)) { 1907 sum = add(sum, term, context); 1908 n += 2; 1909 fact = mul(fact, n*(n-1), context); 1910 powx = mul(powx, sqrx, context); 1911 term = div(powx, fact, context); 1912 } 1913 return round(sum, inContext); 1914 } 1915 1916 1917 unittest 1918 { // sinh 1919 static struct S { TD x; TD expect; } 1920 S[] s = 1921 [ 1922 { "1.0", "1.175201193643801" }, 1923 { "0.5", "0.5210953054937474" }, 1924 { "0.333", "0.3391885521570523" }, 1925 ]; 1926 auto f = FunctionTest!(S,TD)("sinh"); 1927 foreach (t; s) f.test(t, sinh(t.x)); 1928 writefln(f.report); 1929 } 1930 1931 /// Decimal version of std.math function. 1932 public T cosh(T)(T x, Context inContext) if (isDecimal!T) 1933 { 1934 auto context = guard(inContext); 1935 long n = 0; 1936 T sum = 0; 1937 T powx = 1; 1938 T sqrx = sqr(x, context); 1939 T fact = 1; 1940 T term = powx; 1941 while (term.copyAbs > T.epsilon(context)) { 1942 sum = add(sum, term, context); 1943 n += 2; 1944 fact = mul(fact, n*(n-1), context); 1945 powx = mul(powx, sqrx, context); 1946 term = div(powx, fact, context); 1947 } 1948 return round(sum, inContext); 1949 } 1950 1951 unittest 1952 { // cosh 1953 static struct S { TD x; TD expect; } 1954 S[] s = 1955 [ 1956 { "1.0", "1.543080634815244" }, 1957 { "0.5", "1.127625965206381" }, 1958 { "0.333", "1.055958746312751" }, 1959 ]; 1960 auto f = FunctionTest!(S,TD)("cosh"); 1961 foreach (t; s) f.test(t, cosh(t.x)); 1962 writefln(f.report); 1963 } 1964 1965 /// Decimal version of std.math function. 1966 public T tanh(T)(T x, Context inContext) if (isDecimal!T) 1967 { 1968 auto context = guard(inContext); 1969 T tan = div(sinh(x, context), cosh(x, context), context); 1970 return round(tan, inContext); 1971 } 1972 1973 unittest 1974 { // tanh 1975 static struct S { TD x; TD expect; } 1976 S[] s = 1977 [ 1978 { "1.0", "0.7615941559557649" }, 1979 { "0.5", "0.4621171572600098" }, 1980 { "0.333", "0.3212138289885354" }, 1981 ]; 1982 auto f = FunctionTest!(S,TD)("tanh"); 1983 foreach (t; s) f.test(t, tanh(t.x)); 1984 writefln(f.report); 1985 } 1986 1987 mixin (UnaryFunction!("asinh")); 1988 mixin (UnaryFunction!("acosh")); 1989 mixin (UnaryFunction!("atanh")); 1990 1991 /// Decimal version of std.math function. 1992 public T asinh(T)(T x, Context inContext) if (isDecimal!T) 1993 { 1994 if (x.isZero) return T.zero; // TODO: (behavior) special values 1995 auto context = guard(inContext); 1996 // TODO: fix the cutoff -- is the series expansion needed? 1997 if (x.copyAbs >= "1.0E-6") 1998 { 1999 T arg = add(x, sqrt(add(sqr(x, context), T.one, context), context), context); 2000 return round(log(arg, context), inContext); 2001 } 2002 else 2003 { 2004 T sum = 0; 2005 T powx = x; 2006 T sqrx = sqr(x, context); 2007 T n = 1; 2008 T term = x; 2009 T scl = 1; 2010 int count = 0; 2011 while (term.copyAbs > T.epsilon(context)) { 2012 sum = add(sum, term, context); 2013 powx = mul(-powx, sqrx, context); 2014 n += 2; 2015 scl *= (n/(n+1)); 2016 term = mul(T(scl), div(powx, T(n), context), context); 2017 } 2018 return round(sum, inContext); 2019 } 2020 } 2021 2022 unittest 2023 { // asinh 2024 static struct S { TD x; int p; TD expect; } 2025 S[] s = 2026 [ 2027 // TODO: add tests for small and large arguments 2028 { "1.0", 9, "0.881373587020" }, 2029 { "0.5", 9, "0.481211825060" }, 2030 { "0.333", 9, "0.327133906664" }, 2031 { pi!TD, 9, "1.86229574331" }, 2032 { "1.1E-3", 9, "1.1E-3" }, 2033 // FIXTHIS: These tests fail with precision problems. 2034 // { "1.0E-3", 9, "0.000999999998" }, 2035 // { "0.9E-3", 9, "0.000899999878500" }, 2036 // { "1.0E-5", 9, "1.0E-5" }, 2037 // { "1.0E-6", 9, "1.0E-6" }, 2038 // { "1.0E-7", 9, "1.0E-7" }, 2039 { "1.234567E-7", 9, "1.234567E-7" }, 2040 { "0.0", 9, "0.0" }, 2041 ]; 2042 auto f = FunctionTest!(S,TD)("asinh"); 2043 foreach (t; s) f.test(t, asinh(t.x, t.p), t.p); 2044 writefln(f.report); 2045 2046 } 2047 2048 /// Decimal version of std.math function. 2049 public T acosh(T)(T x, Context inContext) if (isDecimal!T) 2050 { 2051 if (x.copyAbs < T.one) { 2052 contextFlags.set(INVALID_OPERATION); 2053 return T.nan; 2054 } 2055 if (x.copyAbs.isOne) { 2056 return T.zero; 2057 } 2058 auto context = guard(inContext); 2059 // TODO: this is imprecise at small arguments -- Taylor series? 2060 T arg = add(x, sqrt(sub(sqr(x, context), T.one, context), context), context); 2061 return round(log(arg, context), inContext); 2062 } 2063 2064 /*unittest 2065 { // acosh 2066 static struct S { TD x; TD expect; } 2067 S[] s = 2068 [ 2069 { "1.0", "0.0" }, 2070 { "2.0", "1.31695790" }, 2071 // FIXTHIS: These tests fail with precision problems. 2072 // { "1.0000001", "0.000447213592" }, 2073 // { "0.0", "NaN" }, 2074 // { "1.5", "0.962423650119" }, 2075 // { "1.333", "0.794987388708" }, 2076 ]; 2077 auto f = FunctionTest!(S,TD)("acosh"); 2078 foreach (t; s) f.test(t, acosh(t.x)); 2079 writefln(f.report); 2080 }*/ 2081 2082 /// Decimal version of std.math function. 2083 public T atanh(T)(T x, Context inContext) if (isDecimal!T) 2084 { 2085 T abs = x.copyAbs; 2086 if (abs > T.one) { 2087 contextFlags.set(INVALID_OPERATION); 2088 return T.nan; 2089 } 2090 if (abs.isOne) { 2091 return T.infinity(x.sign); 2092 } 2093 2094 auto context = guard(inContext, 3); 2095 T cutoff = "1.0E-6"; // cutoff point for switch to series expansion 2096 if(abs > cutoff) 2097 { // atanh(x) = 1/2 * log((1+x)/(1-x)) 2098 T n = add(T.one, x, context); 2099 T d = sub(T.one, x, context); 2100 T q = div(n, d, context); 2101 T l = log(q, context); 2102 T a = mul(T.HALF, l, context); 2103 return round(a, inContext); 2104 } 2105 else 2106 { // series expansion 2107 T sum = 0; 2108 T powx = x; 2109 T sqrx = sqr(x, context); 2110 long dvsr = 1; 2111 T term = powx; 2112 int count = 0; 2113 while (term.copyAbs > T.epsilon(context)) { 2114 sum = add(sum, term, context); 2115 powx = mul(powx, sqrx, context); 2116 dvsr += 2; 2117 term = div!T(powx, dvsr, context); 2118 } 2119 return round(sum, inContext); 2120 } 2121 } 2122 2123 unittest 2124 { // atanh 2125 static struct S { TD x; int p; TD expect; } 2126 S[] s = 2127 [ 2128 { " 1.0", 9, "Infinity" }, 2129 { "-1.0", 9, "-Infinity" }, 2130 { " 1.01", 9, "NaN" }, 2131 { "-1.01", 9, "NaN" }, 2132 { "0.9", 9, "1.47221949" }, 2133 { "0.99999", 9, "6.10303382276" }, 2134 { "1.0E-7", 9, "1.0E-7" }, 2135 { "0.5", 12, "0.549306144334" }, 2136 { "0.333", 11, "0.346198637132" }, 2137 { "0.333", 12, "0.346198637132" }, 2138 ]; 2139 auto f = FunctionTest!(S,TD)("atanh"); 2140 foreach (t; s) f.test(t, atanh!TD(t.x, t.p), t.p); 2141 writefln(f.report); 2142 } 2143 2144 unittest { 2145 writeln("=========================="); 2146 }