1 // Written in the D programming language 2 3 /** 4 * Rounding methods for floating-point decimal arithmetic. 5 * 6 * Authors: Paul D. Anderson 7 * 8 * Copyright: Copyright 2009-2017 by Paul D. Anderson. 9 * 10 * License: <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a> 11 * 12 * Standards: 13 * General Decimal Arithmetic Specification, 14 * Version 1.70, (25 March 2009). 15 */ 16 17 module eris.decimal.rounding; 18 19 import eris.decimal; 20 21 import std.conv; 22 import std..string; 23 import std.traits; 24 import std.stdio; 25 26 unittest { 27 writeln(" rounding tests "); 28 writeln("=========================="); 29 } 30 31 version(unittest) 32 { 33 import std.stdio; 34 import eris.decimal.test; 35 } 36 37 /** 38 * Returns the number rounded to its type precision. 39 * Flags: SUBNORMAL, CLAMPED, OVERFLOW, INEXACT, ROUNDED. 40 */ 41 public D round(D)(in D num) if (isDecimal!D) 42 { 43 return round(num, D.context); 44 } 45 46 /** 47 * Rounds the number to the precision of the context parameter. 48 * if setFlags is false no context flags will be set by this operation. 49 * Returns the rounded number. 50 * Flags: SUBNORMAL, CLAMPED, OVERFLOW, INEXACT, ROUNDED. 51 */ 52 package D round(D)(in D num, Context context = D.context, bool setFlags = true) 53 if (isDecimal!D) 54 { 55 return round(num, 56 context.precision, context.mode, setFlags); 57 } 58 59 /** 60 * Rounds the number to the specified precision using the specified rounding mode. 61 * if setFlags is false none of the context flags will be set by this operation. 62 * Flags: SUBNORMAL, CLAMPED, OVERFLOW, INEXACT, ROUNDED. 63 */ 64 //@safe 65 package D round(D)(in D num, int precision, Round mode = D.mode, 66 bool setFlags = true) 67 if (isDecimal!D) 68 { 69 D copy = num; 70 // check for overflow before rounding and copy the input 71 copy = overflow(num, mode, setFlags); 72 73 // check rounding mode 74 if (mode == ROUND_NONE) return copy; 75 76 // special values aren't rounded 77 if (!copy.isFinite) return copy; 78 79 // handle subnormal numbers 80 if (!copy.isZero && copy.isSubnormal()) 81 { 82 if (setFlags) contextFlags.set(SUBNORMAL); 83 // int diff = copy.adjExpo - copy.tinyExpo; 84 int subprecision = copy.adjExpo - copy.tinyExpo + 1; 85 // use the subnormal precision and round 86 if (precision < subprecision) subprecision = precision; 87 if (copy.digits > subprecision) { 88 copy = modeRound(copy, subprecision, mode, setFlags); 89 } 90 // if the result of rounding a subnormal is zero 91 // the clamped flag is set. (Spec. p. 51) 92 if (copy.adjExpo < copy.tinyExpo) 93 { 94 copy.expo = copy.tinyExpo; 95 if (setFlags) contextFlags.set(CLAMPED); 96 } 97 return copy; 98 } 99 // zero values aren't rounded, but they are checked for 100 // subnormal exponents. 101 if (copy.isZero) 102 { 103 if (copy.expo < copy.minExpo) 104 { 105 if (setFlags) contextFlags.set(SUBNORMAL); 106 if (copy.expo < copy.tinyExpo) 107 { 108 copy.expo = copy.tinyExpo; 109 } 110 } 111 return copy; 112 } 113 114 115 // round the number 116 return modeRound(copy, precision, mode, setFlags); 117 118 } // end round() 119 120 unittest 121 { // round 122 static struct S { D16 x; int n; D16 expect; } 123 S[] s = 124 [ 125 { "9999", 3, "1.00E+4" }, 126 { "1234567890", 3, "1.23E+9" }, 127 { "1234567890", 4, "1.235E+9" }, 128 { "1234567890", 5, "1.2346E+9" }, 129 { "1234567890", 6, "1.23457E+9" }, 130 { "1234567890", 7, "1.234568E+9" }, 131 { "1234567890", 8, "1.2345679E+9" }, 132 { "1235", 3, "1.24E+3" }, 133 { "12359", 3, "1.24E+4" }, 134 { "1245", 3, "1.24E+3" }, 135 { "12459", 3, "1.25E+4" }, 136 // subnormal numbers 137 { "1.234567890123456E-370", 16, "1.23456789012346E-370" }, 138 { "1.234567890123456E-380", 12, "1.2346E-380" }, 139 { "1.234567890123456E-381", 9, "1.235E-381" }, 140 { "1.234567890123456E-381", 3, "1.23E-381" }, 141 { "1.234567890123456E-382", 6, "1.23E-382" }, 142 { "1.234567899123456E-383", 3, "1.2E-383" }, 143 { "1.234567899123456E-384", 6, "1E-384" }, 144 { "1E-369", 3, "1E-369" }, 145 { "1E-384", 3, "1E-384" }, 146 { "1E-385", 3, "0E-384" }, 147 { "0.1E-384", 3, "0E-384" }, 148 { "1234E-385", 3, "1.23E-382" }, 149 // extreme range zeros 150 { "0E+369", 3, "0E-368" }, 151 { "0E+370", 3, "0E-368" }, 152 // TODO: I don't think overrange zeros should have tinyExpo 153 { "0E-368", 3, "0E-368" }, 154 { "0E-369", 3, "0E-369" }, 155 { "0E-384", 3, "0E-384" }, 156 { "0E-385", 3, "0E-384" }, 157 ]; 158 auto f = FunctionTest!(S,D16)("roundPrec"); 159 foreach (t; s) f.test(t, round!D16(t.x,t.n)); 160 writefln(f.report); 161 } 162 163 //-------------------------------- 164 // private methods 165 //-------------------------------- 166 167 // TODO: needs unittests 168 // TODO: return boolean value 169 /** 170 * Returns true if the number is too large to be represented 171 * and adjusts the number according to the rounding mode. 172 * Implements the 'overflow' processing in the specification. (p. 53) 173 * Flags: OVERFLOW, ROUNDED, INEXACT. 174 * Precondition: number must be finite. 175 */ 176 //@safe 177 private D overflow(D)(in D num, Round mode = D.mode, 178 bool setFlags = true) 179 if (isDecimal!D) 180 { 181 if (!num.isFinite) return num; 182 if (num.adjExpo <= D.maxExpo) return num; 183 184 if (setFlags) contextFlags.set(OVERFLOW | INEXACT | ROUNDED); 185 186 switch (mode) 187 { 188 case HALF_UP: 189 case HALF_EVEN: 190 case HALF_DOWN: 191 case ROUND_UP: 192 return D.infinity(num.sign); 193 case ROUND_DOWN: 194 return num.sign ? D.max.copyNegate : D.max; 195 case CEILING: 196 return num.sign ? D.max.copyNegate : D.infinity; 197 case FLOOR: 198 return num.sign ? D.infinity(true) : D.max; 199 default: 200 return num; 201 } 202 } 203 204 // TODO: these tests aren't very useful 205 unittest 206 { // overflow 207 static struct S { D16 num; Round mode; D16 expect; } 208 S[] s = 209 [ 210 { "1000", HALF_EVEN, "1000" }, 211 { "1000000", HALF_EVEN, "10000E+2" }, 212 { "99999", HALF_EVEN, "99999" }, 213 // { "1234550", HALF_EVEN, "1.2346E+6" }, 214 // { "1234550", ROUND_DOWN, "1.2345E+6" }, 215 // { "1234550", ROUND_UP, "1.2346E+6" }, 216 ]; 217 auto f = FunctionTest!(S,D16)("overflow"); 218 foreach (t; s) f.test(t, overflow!D16(t.num,t.mode)); 219 writefln(f.report); 220 } 221 222 /** 223 * Rounds the number to the specified precision 224 * using the specified rounding mode. 225 */ 226 private D modeRound(D)(D num, int precision, Round mode = D.mode, 227 bool setFlags = true) 228 if (isDecimal!D) 229 { 230 // finite numbers only 231 if (!num.isFinite) return num; 232 // check rounding mode 233 if (mode == ROUND_NONE) return num; 234 // if the number is short enough, don't round 235 if (num.digits <= precision) return num; 236 237 D copy = overflow(num, mode, setFlags); 238 // did it overflow ? 239 if (!copy.isFinite) return copy; 240 241 // calculate the remainder 242 D remainder = getRemainder(copy, precision); 243 // if the number wasn't rounded, return 244 if (remainder.isZero) return copy; 245 246 // check for deleted leading zeros in the remainder. 247 // makes a difference only in round-half modes. 248 if (mode == HALF_EVEN || mode == HALF_UP || mode == HALF_DOWN) 249 { 250 if (countDigits(remainder.coff) != remainder.digits) 251 { 252 return copy; 253 } 254 } 255 256 switch (mode) { 257 case ROUND_UP: 258 incrementAndRound(copy); 259 break; 260 case ROUND_DOWN: 261 break; 262 case CEILING: 263 if (!num.sign) incrementAndRound(copy); 264 break; 265 case FLOOR: 266 if (num.sign) incrementAndRound(copy); 267 break; 268 case HALF_UP: 269 if (firstDigit(remainder.coff) >= 5) 270 incrementAndRound(copy); 271 break; 272 case HALF_DOWN: 273 if (testFive(remainder.coff) > 0) 274 incrementAndRound(copy); 275 break; 276 case HALF_EVEN: 277 switch (testFive(remainder.coff)) 278 { 279 case -1: 280 break; 281 case 1: 282 incrementAndRound(copy); 283 break; 284 case 0: 285 if (lastDigit(copy.coff) & 1) { 286 incrementAndRound(copy); 287 } 288 break; 289 default: 290 break; 291 } 292 break; 293 default: 294 break; 295 } // end switch (mode) 296 return overflow(copy, mode, setFlags); 297 } // end modeRound() 298 299 /// 300 unittest 301 { 302 } 303 304 unittest 305 { // modeRound 306 static struct S { D16 x; int p; Round r; D16 expect; } 307 S[] s = 308 [ 309 { "1000", 5, HALF_EVEN, "1000" }, 310 { "1000000", 5, HALF_EVEN, "10000E+2" }, 311 { "99999", 5, HALF_EVEN, "99999" }, 312 { "1234550", 5, ROUND_NONE, "1234550" }, 313 { "1234550", 5, ROUND_UP, "1.2346E+6" }, 314 { "1234550", 5, ROUND_DOWN, "1.2345E+6" }, 315 { "1234550", 5, HALF_UP, "1.2346E+6" }, 316 { "1234550", 5, HALF_EVEN, "1.2346E+6" }, 317 { "1234550", 5, HALF_DOWN, "1.2345E+6" }, 318 { "1234550", 5, FLOOR, "1.2345E+6" }, 319 { "1234550", 5, CEILING, "1.2346E+6" }, 320 ]; 321 auto f = FunctionTest!(S,D16)("modeRound"); 322 foreach (t; s) f.test(t, modeRound!D16(t.x,t.p,t.r)); 323 writefln(f.report); 324 } 325 326 /** 327 * Shortens the coefficient of the number to the specified precision, 328 * adjusts the exponent, and returns the (unsigned) remainder. 329 * If the number is already less than or equal to the precision, the 330 * number is unchanged and the remainder is zero. 331 * Otherwise the rounded flag is set, and if the remainder is not zero 332 * the inexact flag is also set. 333 * Flags: ROUNDED, INEXACT. 334 */ 335 private D getRemainder(D) (ref D num, int precision) if (isDecimal!D) 336 { 337 int diff = num.digits - precision; 338 if (diff <= 0) return D.zero; 339 340 D remainder = D.zero; 341 contextFlags.set(ROUNDED); 342 BigInt divisor = pow10b(diff); 343 BigInt dividend = num.coff; 344 BigInt quotient = dividend/divisor; 345 auto cf = dividend - quotient*divisor; 346 if (cf != 0) { 347 remainder.digits = diff; 348 remainder.expo = num.expo; 349 remainder.coff = cf; 350 contextFlags.set(INEXACT); 351 } 352 num.coff = quotient; 353 num.digits = countDigits(quotient); //precision; 354 num.expo = num.expo + diff; 355 return remainder; 356 } 357 358 unittest 359 { // getRemainder 360 static struct S { D16 x; int p; D16 expect; } 361 S[] s = 362 [ 363 { 1234567890123456L, 5, "67890123456" }, 364 ]; 365 auto f = FunctionTest!(S,D16)("remainder"); 366 foreach (t; s) f.test(t, getRemainder!D16(t.x,t.p)); 367 writefln(f.report); 368 } 369 370 /** 371 * Increments the coefficient by one. 372 * If this causes an overflow the coefficient is adjusted by clipping 373 * the last digit (it will be zero) and incrementing the exponent. 374 */ 375 private void incrementAndRound(D)(ref D num) if (isDecimal!D) 376 { 377 // if num is zero 378 if (num.digits == 0) { 379 num.coff = 1; 380 num.digits = 1; 381 return; 382 } 383 num.coff = num.coff + 1; 384 // TODO: (efficiency) is there a less expensive test? 385 if (lastDigit(num.coff) == 0) { 386 if (num.coff / pow10b(num.digits) > 0) { 387 num.coff = num.coff / 10; 388 num.expo = num.expo + 1; 389 } 390 } 391 } 392 393 unittest 394 { // incrementAndRound 395 D16 incr(D16 num) 396 { 397 incrementAndRound(num); 398 return num; 399 } 400 401 static struct S { D16 num; D16 expect; } 402 S[] s = 403 [ 404 { 10, 11 }, 405 { 19, 20 }, 406 { 999, "1.00E+3" }, 407 ]; 408 auto f = FunctionTest!(S,D16)("increment"); 409 foreach (t; s) f.test(t, incr(t.num)); 410 writefln(f.report); 411 } 412 413 /*unittest { // increment 414 write("-- increment&round.."); 415 D16 actual, expect; 416 actual = 10; 417 expect = 11; 418 incrementAndRound(actual); 419 assertEqual(actual, expect); 420 actual = 19; 421 expect = 20; 422 incrementAndRound(actual); 423 assertEqual(actual, expect); 424 actual = 999; 425 expect = "1.00E+3"; 426 incrementAndRound(actual); 427 assertEqual(actual, expect); 428 writeln("passed"); 429 }*/ 430 431 /// 432 /// Returns a numeric string with the specified precision. 433 public string roundStr(string str, int precision) 434 { 435 436 // make a copy, deleting any whitespace at ends of the string 437 char[] copy = strip(str).dup; 438 439 // if the string has a decimal point increment the precision to account 440 // for the extra character 441 if (copy.indexOf('.') >= 0) precision++; 442 443 // strip out any underscores 444 copy = copy.replace("_", ""); 445 446 // ignore leading zeros 447 size_t index = 0; 448 while (copy[index] == '0') index++; 449 precision += index; 450 451 // if the precision is greater than the length return the whole string 452 if (precision >= copy.length) return str; 453 454 // get the last digit in the (to-be-)clipped string, 455 // and the following digit 456 char last = str[precision-1]; 457 char follow = str[precision]; 458 459 // if the following digit is less than five return the clipped string 460 if (follow < '5') return copy[0..precision].idup; 461 462 // otherwise, increment last digit in the string(round half-up) 463 copy = copy[0..precision]; 464 copy[precision-1] += 1; 465 466 // if the increment resulted in a digit return the clipped string 467 if (copy[precision-1] <= '9') return copy[0..precision].idup; 468 469 // otherwise, (the last digit increment resulted in a non-digit) 470 // set the last digit to '0' and increment its preceding digit, 471 // repeat as necessary 472 int lix = precision - 1; 473 last = copy[lix]; 474 while (last > '9') { 475 copy[lix] = '0'; 476 lix--; 477 copy[lix] += 1; 478 last = copy[lix]; 479 } 480 return copy[0..precision].idup; 481 } 482 483 /** 484 * Returns -1, 1, or 0 if the remainder is less than, more than, 485 * or exactly half the least significant digit of the shortened coefficient. 486 * Exactly half is a five followed by zero or more zero digits. 487 */ 488 private int testFive(in BigInt x) 489 { 490 int num = countDigits(x); 491 BigInt btens = pow10b(num - 1); 492 int first = cast(int)(x / btens); 493 if (first < 5) return -1; 494 if (first > 5) return +1; 495 BigInt zeros = x % btens; 496 return zeros ? 1 : 0; 497 } 498 499 unittest 500 { // testFive 501 static struct S { BigInt x; int expect; } 502 S[] s = 503 [ 504 { "5000000000000000000000", 0 }, 505 { "4999999999999999999999", -1 }, 506 { "50000000000000000000000000000000000000000000000001", 1 }, 507 ]; 508 auto f = FunctionTest!(S,int)("testFive"); 509 foreach (t; s) f.test(t, testFive(t.x)); 510 writefln(f.report); 511 } 512 513 //----------------------------- 514 // useful constants 515 //----------------------------- 516 517 /*private enum BigInt BIG_ZERO = BigInt(0); 518 private enum BigInt BIG_ONE = BigInt(1); 519 private enum BigInt BIG_FIVE = BigInt(5); 520 private enum BigInt BIG_TEN = BigInt(10); 521 private enum BigInt BILLION = BigInt(1_000_000_000); 522 private enum BigInt QUINTILLION = BigInt(1_000_000_000_000_000_000);*/ 523 524 525 /// The maximum number of decimal digits that fit in an int value. 526 //public enum int MAX_INT_DIGITS = 9; 527 /// The maximum decimal value that fits in an int. 528 //public enum uint MAX_DECIMAL_INT = 999999999U; 529 /// The maximum number of decimal digits that fit in a long value. 530 //public enum int MAX_LONG_DIGITS = 18; 531 /// The maximum decimal value that fits in a long value. 532 //public enum ulong MAX_DECIMAL_LONG = 10UL^^MAX_LONG_DIGITS - 1; 533 534 //----------------------------- 535 // BigInt digit functions 536 //----------------------------- 537 538 private enum ten = 10UL; 539 /** 540 * An array of unsigned long integers with values of 541 * powers of ten from 10^^0 to 10^^19 542 */ 543 private enum ulong[20] pow10UL = [ten^^0, 544 ten^^1, ten^^2, ten^^3, ten^^4, ten^^5, ten^^6, 545 ten^^7, ten^^8, ten^^9, ten^^10, ten^^11, ten^^12, 546 ten^^13, ten^^14, ten^^15, ten^^16, ten^^17, ten^^18, ten^^19]; 547 548 /** 549 * Returns the number of decimal digits in a non-negative big integer 550 */ 551 public int countDigits(T)(in T m) 552 if (is(T == BigInt) || isUnsigned!T) 553 { 554 if (m == 0) 555 { 556 return 0; 557 } 558 559 static if (is(T == BigInt)) 560 { 561 if (m.ulongLength == 1) 562 { 563 return countDigits(m.getDigit(0)); 564 } 565 566 int count; 567 ulong n = clipDigits(m, count); 568 return count + countDigits(n); 569 } 570 else 571 { 572 auto n = cast(ulong)m; 573 // special cases: 574 if (n == 0) return 0; 575 if (n < 10) return 1; 576 if (n >= pow10UL[19]) return 20; 577 // use a binary search to count the digits 578 int min = 2; 579 int max = 19; 580 while (min <= max) 581 { 582 int mid = (min + max)/2; 583 if (n < pow10UL[mid]) 584 { 585 max = mid - 1; 586 } 587 else 588 { 589 min = mid + 1; 590 } 591 } 592 return min; 593 } 594 } 595 596 /// 597 unittest // countDigits 598 { 599 auto big = BigInt( 600 "1234567890_1234567890_1234567890_1234567890_1234567890" ~ 601 "1234567890_1234567890_1234567890_1234567890_1234567890_1"); 602 assert(countDigits(big) == 101); 603 } 604 605 unittest 606 { // countDigits 607 static struct S { BigInt n; int expect; } 608 S[] s = 609 [ 610 { "123456", 6 }, 611 { "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901", 101 }, 612 { "1234567890123456789012345678901234567890123456789012", 52 }, 613 ]; 614 auto f = FunctionTest!(S,int)("countDigits"); 615 foreach (t; s) f.test(t, countDigits(t.n)); 616 writefln(f.report); 617 } 618 619 unittest 620 { // countDigits 621 static struct S { ulong n; int expect; } 622 S[] s = 623 [ 624 { 7, 1 }, 625 { 13, 2 }, 626 { 999, 3 }, 627 { 9999, 4 }, 628 { 25978, 5 }, 629 { 2008617, 7 }, 630 { 1234567890, 10 }, 631 { 10000000000, 11 }, 632 { 123456789012345, 15 }, 633 { 1234567890123456, 16 }, 634 {123456789012345678, 18 }, 635 { long.max, 19 }, 636 { ulong.max, 20 }, 637 { ulong.min, 0 }, 638 ]; 639 auto f = FunctionTest!(S,int)("countDigits"); 640 foreach (t; s) f.test(t, countDigits(t.n)); 641 writefln(f.report); 642 } 643 644 645 /// Clips decimal digits until a single ulong digit is left. 646 private ulong clipDigits(in BigInt big, out int count) 647 { 648 if (big <= ulong.max) return cast(ulong)big; 649 650 enum BigInt tenQuint = 10UL^^19; 651 count = 0; 652 BigInt quotient = big; 653 while (quotient > tenQuint) { 654 quotient /= tenQuint; 655 count += 19; 656 } 657 return cast(ulong)quotient; 658 } 659 660 unittest // clipDigits 661 { 662 int count; 663 BigInt big; 664 big = BigInt("123456"); 665 assert(clipDigits(big, count) == 123456); 666 assert(clipDigits(BigInt(ulong.max), count) == ulong.max); 667 big = BigInt("1234567890_1234567890_1234567890_1234567890_1234567890"); 668 assert(clipDigits(big, count) == 123456789012); 669 } 670 671 /// returns the first decimal digit of the number 672 public uint firstDigit(T)(T n) 673 if (is(T == BigInt) || isUnsigned!T) 674 { 675 if (n == 0) return 0; 676 if (n < 10) return cast(int) n; 677 static if (is(T == BigInt)) 678 { 679 int count; 680 return firstDigit(clipDigits(n,count)); 681 } 682 else 683 { 684 int digits = countDigits(n); 685 return cast(uint)(n / pow10UL[digits-1]); 686 } 687 } 688 689 /// 690 unittest 691 { 692 assert(firstDigit(BigInt("8234567890123456789012345678901234567890123")) == 8); 693 assert(firstDigit(0U) == 0); 694 assert(firstDigit(7U) == 7); 695 assert(firstDigit(9999UL) == 9); 696 assert(firstDigit(uint.max) == 4); 697 assert(firstDigit(ulong.max) == 1); 698 } 699 700 unittest // move to regression testing 701 { // firstDigit(BigInt) 702 static struct S { BigInt n; uint expect; } 703 S[] s = 704 [ 705 { "5000000000000000000000", 5 }, 706 { "45500000001209854300023400000000000000", 4 }, 707 { "82345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678905", 8 }, 708 ]; 709 auto f = FunctionTest!(S,uint)("1stDigit(b)"); 710 foreach (t; s) f.test(t, firstDigit(t.n)); 711 writefln(f.report); 712 } 713 714 unittest 715 { // firstDigit(ulong) 716 static struct S { ulong n; uint expect; } 717 S[] s = 718 [ 719 { 0, 0 }, 720 { 7, 7 }, 721 { 13, 1 }, 722 { 999, 9 }, 723 { 9999, 9 }, 724 { 25987, 2 }, 725 { 5008617, 5 }, 726 { 3234567890, 3 }, 727 { 10000000000, 1 }, 728 { 823456789012345, 8 }, 729 { 4234567890123456, 4 }, 730 { 623456789012345678, 6 }, 731 {long.max, 9 } 732 ]; 733 auto f = FunctionTest!(S,uint)("1stDigit(u)"); 734 foreach (t; s) f.test(t, firstDigit(t.n)); 735 writefln(f.report); 736 } 737 738 /** 739 * Decimal shift left. 740 * Shifts the number left by the specified number of decimal digits. 741 * If n == 0 the number is returned unchanged. 742 * If n < 0 the number is shifted right. 743 */ 744 public BigInt shiftBig(BigInt num, int n) 745 { 746 if (n > 0) { 747 num *= pow10b(n); 748 } 749 if (n < 0) { 750 num /= pow10b(-n); 751 } 752 return num; 753 } 754 755 /// 756 unittest { // shiftBig(BigInt) 757 BigInt big; 758 int n; 759 big = 12345; 760 n = 2; 761 assert(shiftBig(big, n) == 1234500); 762 big = 1234567890; 763 n = 7; 764 assert(shiftBig(big, n) == BigInt(12345678900000000)); 765 big = 12; 766 n = 2; 767 assert(shiftBig(big, n) == 1200); 768 big = 12; 769 n = 4; 770 assert(shiftBig(big, n) == 120000); 771 BigInt res; 772 big = BigInt("9223372036854775807"); 773 n = -10; 774 assert(shiftBig(big, n) == BigInt("922337203")); 775 big = BigInt("9223372036854775808"); 776 n = -10; 777 assert(shiftBig(big, n) == BigInt("922337203")); 778 } 779 780 /// Returns the last digit of the argument. 781 public int lastDigit(in BigInt big) 782 { 783 auto digit = big % BigInt(10); 784 if (digit < 0) digit = -digit; 785 return digit.toInt; 786 } 787 788 unittest 789 { // lastDigit 790 static struct S { BigInt n; int expect; } 791 S[] s = 792 [ 793 { 7, 7 }, 794 { -13, 3 }, 795 { 999, 9 }, 796 { -9999, 9 }, 797 { 25987, 7 }, 798 { -5008615, 5 }, 799 { 3234567893, 3 }, 800 { -10000000000, 0 }, 801 { 823456789012348, 8 }, 802 { 4234567890123456, 6 }, 803 { 623456789012345674, 4 }, 804 { long.max, 7 }, 805 ]; 806 auto f = FunctionTest!(S,int)("lastDigit"); 807 foreach (t; s) f.test(t, lastDigit(t.n)); 808 writefln(f.report); 809 } 810 811 /// Returns the number of trailing zeros in the argument. 812 public uint trailingZeros(BigInt n, uint digits) 813 { 814 // shortcuts for frequent values 815 if (n == 0) return 0; 816 if (n % 10) return 0; 817 if (n % 100) return 1; 818 // find by binary search 819 uint min = 3; 820 uint max = digits - 1; 821 while (min <= max) { 822 int mid = (min + max)/2; 823 if (n % pow10b(mid) != 0) 824 { 825 max = mid - 1; 826 } 827 else 828 { 829 min = mid + 1; 830 } 831 } 832 return max; 833 } 834 /// 835 unittest 836 { 837 auto big = BigInt("1234567890123456789012300000000000"); 838 auto digits = countDigits(big); 839 assert(digits == 34); 840 auto zeros = trailingZeros(big, digits); 841 assert(zeros == 11); 842 } 843 844 845 /// Trims any trailing zeros and returns the number of zeros trimmed. 846 public int clipZeros(ref BigInt n, uint digits) 847 { 848 if (digits <= 0) return 0; 849 auto zeros = trailingZeros(n, digits); 850 if (zeros == 0) return 0; 851 n /= pow10b(zeros); 852 return zeros; 853 } 854 855 /// 856 unittest { 857 auto big = BigInt("123456789000000"); 858 auto digits = countDigits(big); 859 assert(digits == 15); 860 auto zeros = clipZeros(big, digits); 861 assert(zeros == 6); 862 } 863 864 unittest { 865 writeln("=========================="); 866 } 867