Mercurial > projects > decimal
comparison src/decimal/bcd.d @ 9:48d564218e05
Added rounding, additional unit tests, strip leading and trailing zeros.
author | Paul (paul.d.anderson@comcast.net) |
---|---|
date | Wed, 24 Mar 2010 22:21:38 -0700 |
parents | c991b9fde45c |
children | f925fe996255 |
comparison
equal
deleted
inserted
replaced
8:c991b9fde45c | 9:48d564218e05 |
---|---|
216 * padding or truncating if necessary. | 216 * padding or truncating if necessary. |
217 * If truncating, first leading zeros are stripped away, then | 217 * If truncating, first leading zeros are stripped away, then |
218 * the remainder is clipped. | 218 * the remainder is clipped. |
219 */ | 219 */ |
220 void setNumDigits(uint n) { | 220 void setNumDigits(uint n) { |
221 this = stripLeadingZeros(this); | 221 stripl(this); |
222 if (n > digits.length) { | 222 if (n > digits.length) { |
223 digits.length = n; | 223 digits.length = n; |
224 } | 224 } |
225 else { | 225 else { |
226 this = truncate(this, n); | 226 this = truncate(this, n); |
341 /** | 341 /** |
342 * Returns true if the internal representation of | 342 * Returns true if the internal representation of |
343 * this BCD integer has leading zeros. | 343 * this BCD integer has leading zeros. |
344 */ | 344 */ |
345 const bool hasLeadingZeros() { | 345 const bool hasLeadingZeros() { |
346 return numDigits > 0 && firstDigit == 0; | 346 return numDigits > 1 && firstDigit == 0; |
347 } | |
348 | |
349 unittest { | |
350 write("hasLeadingZeros..."); | |
351 assert(!ZERO.hasLeadingZeros); | |
352 Bcd b = Bcd(0); | |
353 assert(!b.hasLeadingZeros); | |
354 b = "-00000001"; | |
355 assert(b.hasLeadingZeros); | |
356 b = "-00000000"; | |
357 assert(b.hasLeadingZeros); | |
358 writeln("passed"); | |
347 } | 359 } |
348 | 360 |
349 /** | 361 /** |
350 * Returns true if the value of this BCD integer is zero. | 362 * Returns true if the value of this BCD integer is zero. |
351 * Ignores and leading zeros and the sign of the number. | 363 * Ignores leading zeros and sign. |
352 */ | 364 */ |
353 const bool isZero() { | 365 const bool isZero() { |
354 Bcd temp = stripLeadingZeros(this); | 366 Bcd b = canonical(this); |
355 if (temp.numDigits > 1) return false; | 367 if (b.numDigits > 1) return false; |
356 return temp.lastDigit == 0; | 368 return b.lastDigit == 0; |
369 } | |
370 | |
371 unittest { | |
372 write("isZero........."); | |
373 assert(ZERO.isZero); | |
374 Bcd b = Bcd(0); | |
375 assert(b.isZero); | |
376 b = "-00000001"; | |
377 assert(!b.isZero); | |
378 b = "-00000000"; | |
379 assert(b.isZero); | |
380 writeln("passed"); | |
357 } | 381 } |
358 | 382 |
359 //-------------------------------- | 383 //-------------------------------- |
360 // Assignment operators. | 384 // Assignment operators. |
361 //-------------------------------- | 385 //-------------------------------- |
381 * Converts the integer argument into a BCD integer and assigns it | 405 * Converts the integer argument into a BCD integer and assigns it |
382 * to this BCD integer. | 406 * to this BCD integer. |
383 */ | 407 */ |
384 void opAssign(const long n) { | 408 void opAssign(const long n) { |
385 this = Bcd(n); | 409 this = Bcd(n); |
386 /+ uint m; | |
387 if (n < 0) { | |
388 sign = true; | |
389 m = std.math.abs(n); | |
390 } | |
391 else { | |
392 sign = false; | |
393 m = n; | |
394 } | |
395 Digit[20] dig; | |
396 int i = 0; | |
397 do { | |
398 uint q = m/10; | |
399 uint r = m%10; | |
400 dig[i] = cast(Digit) r; | |
401 m = q; | |
402 i++; | |
403 } while (m > 0); | |
404 digits = dig[0..i].dup;+/ | |
405 } | 410 } |
406 | 411 |
407 | 412 |
408 // index operator | 413 // index operator |
409 Digit opIndex(uint index) { | 414 Digit opIndex(uint index) { |
413 // index assign operator | 418 // index assign operator |
414 void opIndexAssign(Digit value, uint index) { | 419 void opIndexAssign(Digit value, uint index) { |
415 this.digits[index] = value; | 420 this.digits[index] = value; |
416 } | 421 } |
417 | 422 |
418 /+ int opApply(int delegate (ref Digit) dg) { | |
419 int result = 0; | |
420 for (int i = 0; i < numDigits; i++) { | |
421 result = dg(digits[i]); | |
422 if (result) break; | |
423 } | |
424 return result; | |
425 } | |
426 | |
427 int opApply(int delegate (ref Digit) dg) { | |
428 int result = 0; | |
429 for (int i = 0; i < numDigits; i++) { | |
430 result = dg(digits[i]); | |
431 if (result) break; | |
432 } | |
433 return result; | |
434 } | |
435 | |
436 int opApplyReverse(int delegate (ref Digit) dg) { | |
437 int result = 0; | |
438 for (int i = 0; i < numDigits; i++) { | |
439 result = dg(digits[i]); | |
440 if (result) break; | |
441 } | |
442 return result; | |
443 }+/ | |
444 | |
445 //-------------------------------- | 423 //-------------------------------- |
446 // unary operators | 424 // unary operators |
447 //-------------------------------- | 425 //-------------------------------- |
448 | 426 |
449 const Bcd opPos() { | 427 const Bcd opPos() { |
450 return this.dup; | 428 return this.dup; |
451 } | 429 } |
452 | 430 |
453 const Bcd opNeg() { | 431 const Bcd opNeg() { |
454 Bcd copy = this.dup; | 432 return negate(this); |
455 copy.sign = !this.sign; | |
456 return copy; | |
457 } | 433 } |
458 | 434 |
459 const Bcd opCom() { | 435 const Bcd opCom() { |
460 return twosComp(this); | 436 return twosComp(this); |
461 } | 437 } |
649 const int opCmp(T)(const T that) { | 625 const int opCmp(T)(const T that) { |
650 return opCmp(Bcd(that)); | 626 return opCmp(Bcd(that)); |
651 } | 627 } |
652 | 628 |
653 const bool opEquals(T:Bcd)(const T that) { | 629 const bool opEquals(T:Bcd)(const T that) { |
654 Bcd a = stripLeadingZeros(this); | 630 Bcd a = canonical(this); |
655 Bcd b = stripLeadingZeros(that); | 631 Bcd b = canonical(that); |
632 // TODO: this is wrong -- signs can differ -0 and + 0 | |
656 if (this.sign != that.sign) return false; | 633 if (this.sign != that.sign) return false; |
657 if (a.digits.length != b.digits.length) return false; | 634 if (a.digits.length != b.digits.length) return false; |
658 foreach_reverse(int i, Digit digit; a.digits) { | 635 foreach_reverse(int i, Digit digit; a.digits) { |
659 if (digit != b.digits[i]) return false; | 636 if (digit != b.digits[i]) return false; |
660 } | 637 } |
661 return true; | 638 return true; |
662 // return compare(this, that) == 0; | |
663 } | 639 } |
664 | 640 |
665 const bool opEquals(T)(const T that) { | 641 const bool opEquals(T)(const T that) { |
666 return opEquals(Bcd(that)); | 642 return opEquals(Bcd(that)); |
667 } | 643 } |
682 writeln("passed"); | 658 writeln("passed"); |
683 } | 659 } |
684 | 660 |
685 } // end struct Bcd | 661 } // end struct Bcd |
686 | 662 |
663 | |
664 public bool isCanonical(const Bcd a) { | |
665 // no leading zeros | |
666 if (a.hasLeadingZeros) return false; | |
667 // not -0 | |
668 if (a.numDigits == 1 && a.firstDigit == 0) return !a.sign; | |
669 return true; | |
670 } | |
671 | |
672 // Strips leading zeros and changes sign if == -0 | |
673 public Bcd canonical(const Bcd a) { | |
674 Bcd d = a.dup; | |
675 if (isCanonical(a)) return d; | |
676 stripl(d); | |
677 if (d.numDigits == 1 && d.firstDigit == 0) { | |
678 d.sign = false; | |
679 } | |
680 return d; | |
681 } | |
682 | |
683 /** | |
684 * Strips leading zeros from the specified decint. | |
685 */ | |
686 private void stripl(ref Bcd a) { | |
687 if (a.hasLeadingZeros) { | |
688 foreach_reverse(int i, Digit digit; a.digits) { | |
689 if (i == 0 || digit != 0) { | |
690 a.digits.length = i+1; | |
691 break; | |
692 } | |
693 } | |
694 } | |
695 } | |
696 | |
697 unittest { | |
698 write("stripl......"); | |
699 Bcd b = Bcd("+00023"); | |
700 assert(b.toString == "00023"); | |
701 stripl(b); | |
702 assert(b.toString == "23"); | |
703 b = "-00050432"; | |
704 stripl(b); | |
705 assert(b.toString == "-50432"); | |
706 b = "-000"; | |
707 stripl(b); | |
708 assert(b.toString == "-0"); | |
709 writeln("passed"); | |
710 } | |
711 | |
712 /** | |
713 * Strips trailing zeros from the specified decint. | |
714 */ | |
715 private void stripr(ref Bcd a) { | |
716 int len = a.digits.length; | |
717 foreach(int i, Digit digit; a.digits) { | |
718 if (i == len-1 || digit != 0) { | |
719 a.digits = a.digits[i..$]; | |
720 break; | |
721 } | |
722 } | |
723 } | |
724 | |
725 unittest { | |
726 write("stripl......"); | |
727 Bcd b; | |
728 b = 23; | |
729 stripr(b); | |
730 assert(b == 23); | |
731 b = "-5000"; | |
732 stripr(b); | |
733 assert(b.toString == "-5"); | |
734 b = "-000"; | |
735 stripr(b); | |
736 assert(b.toString == "-0"); | |
737 writeln("passed"); | |
738 } | |
739 | |
687 //public string format(const Bcd bcd, bool showPlus = false, bool showLeadingZeros = false) { | 740 //public string format(const Bcd bcd, bool showPlus = false, bool showLeadingZeros = false) { |
688 public string format(const Bcd bcd) { | 741 public string format(const Bcd bcd) { |
689 int len = bcd.digits.length; | 742 int len = bcd.digits.length; |
690 if (bcd.isSigned) len++; | 743 if (bcd.isSigned) len++; |
691 // if (bcd.isSigned || bcd.hasLeadingZeros) len++; | 744 // if (bcd.isSigned || bcd.hasLeadingZeros) len++; |
748 bcd.digits.length = index; | 801 bcd.digits.length = index; |
749 foreach(int i, char ch; str2) { | 802 foreach(int i, char ch; str2) { |
750 bcd.digits[i] = cast(Digit)(ch - '0'); | 803 bcd.digits[i] = cast(Digit)(ch - '0'); |
751 } | 804 } |
752 | 805 |
753 if (!lz) return stripLeadingZeros(bcd); | 806 if (!lz) return canonical(bcd); |
754 return bcd; | 807 return bcd; |
755 } | 808 } |
756 | 809 |
757 private bool isDigit(const char ch) { | 810 private bool isDigit(const char ch) { |
758 return (ch >= '0' && ch <= '9'); | 811 return (ch >= '0' && ch <= '9'); |
759 } | 812 } |
760 | 813 |
761 public Bcd negate(const Bcd a) { | 814 public Bcd negate(const Bcd a) { |
762 return -a; | 815 Bcd b = a.dup; |
816 b.sign = !a.sign; | |
817 return b; | |
763 } | 818 } |
764 | 819 |
765 public Bcd abs(const Bcd a) { | 820 public Bcd abs(const Bcd a) { |
766 return a.isSigned ? -a: +a; | 821 return a.isSigned ? -a: +a; |
767 } | 822 } |
768 | 823 |
769 public int compare(const Bcd m, const Bcd n) { | 824 public int compare(const Bcd m, const Bcd n) { |
770 Bcd a = stripLeadingZeros(m); | 825 Bcd a = canonical(m); |
771 Bcd b = stripLeadingZeros(n); | 826 Bcd b = canonical(n); |
772 if (!a.isSigned && b.isSigned) return 1; | 827 if (!a.isSigned && b.isSigned) return 1; |
773 if (a.isSigned && !b.isSigned) return -1; | 828 if (a.isSigned && !b.isSigned) return -1; |
774 if (a.digits.length > b.digits.length) return 1; | 829 if (a.digits.length > b.digits.length) return 1; |
775 if (a.digits.length < b.digits.length) return -1; | 830 if (a.digits.length < b.digits.length) return -1; |
776 foreach_reverse(int i, Digit digit; a.digits) { | 831 foreach_reverse(int i, Digit digit; a.digits) { |
778 if (digit < b.digits[i]) return -1; | 833 if (digit < b.digits[i]) return -1; |
779 } | 834 } |
780 return 0; | 835 return 0; |
781 } | 836 } |
782 | 837 |
783 // TODO: does this need a const and a dup? | |
784 // should it be public?? private would be okay | |
785 public Bcd stripLeadingZeros(const Bcd a) { | |
786 Bcd d = a.dup; | |
787 if (!a.hasLeadingZeros) return d; | |
788 int len = a.numDigits; | |
789 int i = 0; | |
790 while(i < len-1 && a.getDigit(i) == 0) { | |
791 i++; | |
792 } | |
793 d.digits.length = (len-i); // - 1); | |
794 if (d.numDigits == 1 && d.firstDigit == 0) { | |
795 d.sign = false; | |
796 } | |
797 return d; | |
798 } | |
799 | |
800 unittest { | 838 unittest { |
801 write("strip..."); | 839 write("strip..."); |
802 Bcd bcd; | 840 Bcd bcd; |
803 bcd = "+00123"; | 841 bcd = "+00123"; |
804 assert(bcd.toString == "00123"); | 842 assert(bcd.toString == "00123"); |
805 bcd = stripLeadingZeros(bcd); | 843 bcd = canonical(bcd); |
806 // writeln("bcd = ", bcd); | 844 // writeln("bcd = ", bcd); |
807 assert(bcd.toString == "123"); | 845 assert(bcd.toString == "123"); |
808 bcd = "+000"; | 846 bcd = "+000"; |
809 // writeln("bcd = ", bcd); | 847 // writeln("bcd = ", bcd); |
810 assert(bcd.toString == "000"); | 848 assert(bcd.toString == "000"); |
811 bcd = stripLeadingZeros(bcd); | 849 bcd = canonical(bcd); |
812 // writeln("bcd = ", bcd); | 850 // writeln("bcd = ", bcd); |
813 writeln("passed"); | 851 writeln("passed"); |
814 } | 852 } |
815 | 853 |
816 public bool sameLength(const Bcd a, const Bcd b) { | 854 public bool sameLength(const Bcd a, const Bcd b) { |
939 Bcd a = x.dup; | 977 Bcd a = x.dup; |
940 Bcd b = y.dup; | 978 Bcd b = y.dup; |
941 Bcd sum; | 979 Bcd sum; |
942 if (a.sign == b.sign) { | 980 if (a.sign == b.sign) { |
943 sum = simpleAdd(a, b); | 981 sum = simpleAdd(a, b); |
944 sum = stripLeadingZeros(sum); | 982 stripl(sum); |
945 sum.sign = a.sign; | 983 sum.sign = a.sign; |
946 return sum; | 984 return sum; |
947 } | 985 } |
948 else { // signs differ | 986 else { // signs differ |
949 bool sign; | 987 bool sign; |
962 if (sum.firstDigit == 1) { | 1000 if (sum.firstDigit == 1) { |
963 sum.digits = sum.digits[0..$-1]; | 1001 sum.digits = sum.digits[0..$-1]; |
964 } | 1002 } |
965 else { | 1003 else { |
966 sum = tensComp(sum); | 1004 sum = tensComp(sum); |
967 sum = stripLeadingZeros(sum); | |
968 } | 1005 } |
1006 stripl(sum); | |
969 sum.sign = sign; | 1007 sum.sign = sign; |
970 } | 1008 } |
971 return stripLeadingZeros(sum); | 1009 return sum; |
972 } | 1010 } |
973 | 1011 |
974 unittest { | 1012 unittest { |
975 write("add...."); | 1013 write("add...."); |
976 Bcd a, b; | 1014 Bcd a, b; |
1027 } | 1065 } |
1028 if (carry) { | 1066 if (carry) { |
1029 sum[i] = 1; | 1067 sum[i] = 1; |
1030 } | 1068 } |
1031 else { | 1069 else { |
1032 sum = stripLeadingZeros(sum); | 1070 stripl(sum); |
1033 } | 1071 } |
1034 return sum; | 1072 return sum; |
1035 } | 1073 } |
1074 | |
1075 // TODO: need a clipFirst, clipLast method | |
1036 | 1076 |
1037 /** | 1077 /** |
1038 * Adds two digits and a carry digit. | 1078 * Adds two digits and a carry digit. |
1039 * Returns the (single-digit) sum and sets or resets the carry digit. | 1079 * Returns the (single-digit) sum and sets or resets the carry digit. |
1040 **/ | 1080 **/ |
1110 // TODO: there are lots of ways to speed this up. | 1150 // TODO: there are lots of ways to speed this up. |
1111 public Bcd multiply(const Bcd a, const Bcd b) { | 1151 public Bcd multiply(const Bcd a, const Bcd b) { |
1112 Bcd n, m; | 1152 Bcd n, m; |
1113 Bcd p = Bcd(0, a.numDigits + b.numDigits); | 1153 Bcd p = Bcd(0, a.numDigits + b.numDigits); |
1114 if (a.numDigits > b.numDigits) { | 1154 if (a.numDigits > b.numDigits) { |
1115 n = stripLeadingZeros(a); | 1155 n = canonical(a); |
1116 m = stripLeadingZeros(b); | 1156 m = canonical(b); |
1117 } | 1157 } |
1118 else { | 1158 else { |
1119 n = stripLeadingZeros(b); | 1159 n = canonical(b); |
1120 m = stripLeadingZeros(a); | 1160 m = canonical(a); |
1121 } | 1161 } |
1122 foreach(uint i, Digit d; m.digits) { | 1162 foreach(uint i, Digit d; m.digits) { |
1123 if (d == 0) continue; | 1163 if (d == 0) continue; |
1124 // writeln("mul(n, m[i]) = ", mul(n, m[i])); | 1164 // writeln("mul(n, m[i]) = ", mul(n, m[i])); |
1125 // writeln("pow10(mul(n, m[i]), i) = ", pow10(mul(n, m[i]), i)); | 1165 // writeln("pow10(mul(n, m[i]), i) = ", pow10(mul(n, m[i]), i)); |
1184 assert(d == 2); | 1224 assert(d == 2); |
1185 assert(c == 7); | 1225 assert(c == 7); |
1186 writeln("passed"); | 1226 writeln("passed"); |
1187 } | 1227 } |
1188 | 1228 |
1189 public Bcd truncate(const Bcd a, int n) { | |
1190 Bcd dummy; | |
1191 return truncate(a, n, dummy); | |
1192 } | |
1193 | |
1194 unittest { | |
1195 write("truncate...."); | |
1196 Bcd a; | |
1197 a = 1234567; | |
1198 assert(truncate(a,3) == 123); | |
1199 a = 10256; | |
1200 assert(truncate(a,5) == 10256); | |
1201 a = 8500; | |
1202 assert(truncate(a,1) == 8); | |
1203 a = -8500; | |
1204 assert(truncate(a,1) == -8); | |
1205 a = -8500; | |
1206 assert(truncate(a,-1) == 0); | |
1207 writeln("passed"); | |
1208 } | |
1209 | |
1210 /** | |
1211 * Truncate to the specified number of digits | |
1212 */ | |
1213 public Bcd truncate(const Bcd a, int n, out Bcd r) { | |
1214 if (n <= 0) { | |
1215 r = stripLeadingZeros(a); | |
1216 return ZERO.dup; | |
1217 } | |
1218 if (n >= a.numDigits) { | |
1219 r = ZERO.dup; | |
1220 return stripLeadingZeros(a); | |
1221 } | |
1222 Bcd b; | |
1223 b.digits = a.digits[$-n..$].dup; | |
1224 b.sign = a.sign; | |
1225 r.digits = a.digits[0..$-n].dup; | |
1226 return b; | |
1227 } | |
1228 | |
1229 unittest { | |
1230 write("truncRem..."); | |
1231 Bcd a; | |
1232 Bcd r; | |
1233 a = 1234567; | |
1234 assert(truncate(a, 3, r) == 123); | |
1235 assert(r == 4567); | |
1236 a = 10256; | |
1237 assert(truncate(a,5,r) == 10256); | |
1238 assert(r == 0); | |
1239 a = 8500; | |
1240 assert(truncate(a,1,r) == 8); | |
1241 assert(r == 500); | |
1242 a = -8500; | |
1243 assert(truncate(a,1,r) == -8); | |
1244 assert(r == 500); | |
1245 a = -8500; | |
1246 // writeln("truncate(a, 6, r) = ", truncate(a, 6, r)); | |
1247 // writeln("r = ", r); | |
1248 assert(truncate(a,6,r) == -8500); | |
1249 assert(r == 0); | |
1250 writeln("passed"); | |
1251 } | |
1252 | |
1253 public Bcd pad(Bcd a, int n) { | |
1254 if (n <= a.numDigits) return a.dup; | |
1255 return Bcd(a,n); | |
1256 } | |
1257 | |
1258 unittest { | |
1259 write("pad........."); | |
1260 Bcd a; | |
1261 a = 1234567; | |
1262 assert(pad(a,3) == 1234567); | |
1263 a = 10256; | |
1264 // writeln("pad(a,9) = ", pad(a,9)); | |
1265 assert(pad(a,9).toString == "000010256"); | |
1266 a = 8500; | |
1267 assert(pad(a,6).toString == "008500"); | |
1268 a = -8500; | |
1269 assert(pad(a,6).toString == "-008500"); | |
1270 writeln("passed"); | |
1271 } | |
1272 public Bcd divide(const Bcd a, const Bcd b, out Bcd r) { | 1229 public Bcd divide(const Bcd a, const Bcd b, out Bcd r) { |
1273 | 1230 |
1274 if (b == ZERO) throw new Exception("Divide by zero"); | 1231 if (b == ZERO) throw new Exception("Divide by zero"); |
1275 if (a == ZERO) return ZERO.dup; | 1232 if (a == ZERO) return ZERO.dup; |
1276 | 1233 |
1277 Bcd d = stripLeadingZeros(abs(b)); | 1234 Bcd d = canonical(abs(b)); |
1278 r = stripLeadingZeros(abs(a)); | 1235 r = canonical(abs(a)); |
1279 Bcd q; | 1236 Bcd q; |
1280 Bcd p = d; | 1237 Bcd p = d; |
1281 Bcd m; | 1238 Bcd m; |
1282 | 1239 |
1283 // shift the divisor | 1240 // shift the divisor |
1349 assert(r == -1); | 1306 assert(r == -1); |
1350 writeln("passed"); | 1307 writeln("passed"); |
1351 } | 1308 } |
1352 | 1309 |
1353 | 1310 |
1311 public Bcd truncate(const Bcd a, int n) { | |
1312 Bcd dummy; | |
1313 return truncate(a, n, dummy); | |
1314 } | |
1315 | |
1316 unittest { | |
1317 write("truncate...."); | |
1318 Bcd a; | |
1319 a = 1234567; | |
1320 assert(truncate(a,3) == 123); | |
1321 a = 10256; | |
1322 assert(truncate(a,5) == 10256); | |
1323 a = 8500; | |
1324 assert(truncate(a,1) == 8); | |
1325 a = -8500; | |
1326 assert(truncate(a,1) == -8); | |
1327 a = -8500; | |
1328 assert(truncate(a,-1) == 0); | |
1329 writeln("passed"); | |
1330 } | |
1331 | |
1332 /** | |
1333 * Truncate to the specified number of digits | |
1334 */ | |
1335 public Bcd truncate(const Bcd a, int n, out Bcd r) { | |
1336 if (n <= 0) { | |
1337 r = canonical(a); | |
1338 return ZERO.dup; | |
1339 } | |
1340 if (n >= a.numDigits) { | |
1341 r = ZERO.dup; | |
1342 return canonical(a); | |
1343 } | |
1344 Bcd b; | |
1345 b.digits = a.digits[$-n..$].dup; | |
1346 b.sign = a.sign; | |
1347 r.digits = a.digits[0..$-n].dup; | |
1348 return b; | |
1349 } | |
1350 | |
1351 unittest { | |
1352 write("truncRem..."); | |
1353 Bcd a; | |
1354 Bcd r; | |
1355 a = 1234567; | |
1356 assert(truncate(a, 3, r) == 123); | |
1357 assert(r == 4567); | |
1358 a = 10256; | |
1359 assert(truncate(a,5,r) == 10256); | |
1360 assert(r == 0); | |
1361 a = 8500; | |
1362 assert(truncate(a,1,r) == 8); | |
1363 assert(r == 500); | |
1364 a = -8500; | |
1365 assert(truncate(a,1,r) == -8); | |
1366 assert(r == 500); | |
1367 a = -8500; | |
1368 // writeln("truncate(a, 6, r) = ", truncate(a, 6, r)); | |
1369 // writeln("r = ", r); | |
1370 assert(truncate(a,6,r) == -8500); | |
1371 assert(r == 0); | |
1372 writeln("passed"); | |
1373 } | |
1374 | |
1375 public Bcd pad(Bcd a, int n) { | |
1376 if (n <= a.numDigits) return a.dup; | |
1377 return Bcd(a,n); | |
1378 } | |
1379 | |
1380 unittest { | |
1381 write("pad........."); | |
1382 Bcd a; | |
1383 a = 1234567; | |
1384 assert(pad(a,3) == 1234567); | |
1385 a = 10256; | |
1386 // writeln("pad(a,9) = ", pad(a,9)); | |
1387 assert(pad(a,9).toString == "000010256"); | |
1388 a = 8500; | |
1389 assert(pad(a,6).toString == "008500"); | |
1390 a = -8500; | |
1391 assert(pad(a,6).toString == "-008500"); | |
1392 writeln("passed"); | |
1393 } | |
1394 | |
1354 public Bcd pow10(const Bcd a, uint n) { | 1395 public Bcd pow10(const Bcd a, uint n) { |
1355 Bcd b = stripLeadingZeros(a); | 1396 Bcd b = canonical(a); |
1356 if (n == 0 || b == 0) return b; | 1397 if (n == 0 || b == 0) return b; |
1357 Digit[] zeros = new Digit[n]; | 1398 Digit[] zeros = new Digit[n]; |
1358 b.digits = zeros ~ b.digits; | 1399 b.digits = zeros ~ b.digits; |
1359 return b; | 1400 return b; |
1360 } | 1401 } |
1375 assert(a == Bcd("1000000000")); | 1416 assert(a == Bcd("1000000000")); |
1376 writeln("passed"); | 1417 writeln("passed"); |
1377 } | 1418 } |
1378 | 1419 |
1379 public Bcd shift(const Bcd a, int n) { | 1420 public Bcd shift(const Bcd a, int n) { |
1380 if (n == 0) return stripLeadingZeros(a); | 1421 if (n == 0) return canonical(a); |
1381 if (n > 0) { // shift left | 1422 if (n > 0) { // shift left |
1382 return pow10(a, n); | 1423 return pow10(a, n); |
1383 } | 1424 } |
1384 else { // shift right | 1425 else { // shift right |
1385 if (-n >= a.numDigits) return ZERO.dup; | 1426 if (-n >= a.numDigits) return ZERO.dup; |
1482 | 1523 |
1483 /** | 1524 /** |
1484 * Rounds the specified integer to the specified number of digits. | 1525 * Rounds the specified integer to the specified number of digits. |
1485 * The rounding mode governs the method of rounding. | 1526 * The rounding mode governs the method of rounding. |
1486 */ | 1527 */ |
1487 public Bcd round(Bcd a, int n, Rounding mode) { | 1528 public Bcd round(const Bcd a, int n, Rounding mode) { |
1488 Bcd r; | 1529 Bcd r; |
1489 Bcd b = truncate(a, n, r); | 1530 Bcd b = truncate(a, n, r); |
1490 return b; | 1531 return round(b, r, mode); |
1532 // return b; | |
1491 } | 1533 } |
1492 | 1534 |
1493 /** | 1535 /** |
1494 * Rounds the (truncated) integer based on its remainder and the rounding | 1536 * Rounds the (truncated) integer based on its remainder and the rounding |
1495 * mode. | 1537 * mode. |
1496 */ | 1538 */ |
1497 public Bcd round(Bcd a, Bcd r, Rounding mode) { | 1539 public Bcd round(const Bcd a, const Bcd r, const Rounding mode) { |
1498 Bcd b = a.dup; | 1540 Bcd b = canonical(a); |
1541 ubyte rfd = r.firstDigit; | |
1499 switch (mode) { | 1542 switch (mode) { |
1500 case Rounding.DOWN: | 1543 case Rounding.DOWN: |
1501 return b; | 1544 return b; |
1502 | 1545 |
1503 case Rounding.HALF_UP: | 1546 case Rounding.HALF_UP: |
1504 if (r.firstDigit >= 5) b++; | 1547 if (rfd >= 5) b++; |
1505 return b; | 1548 return b; |
1506 | 1549 |
1507 case Rounding.HALF_EVEN: | 1550 case Rounding.HALF_EVEN: |
1508 /+ BigInt test = 5 * pow10(numDigits(remainder,1)-1); | 1551 if (rfd < 5) return b; |
1509 int result = remainder.opCmp(test); | 1552 if (rfd > 5) return ++b; |
1510 if (result > 0) { | 1553 Bcd p = r.dup; |
1511 increment(number.ceff); | 1554 stripr(p); |
1512 return; | 1555 if (p.numDigits > 1) return ++b; |
1513 } | 1556 // to reach this point the remainder must be exactly 5 |
1514 if (result < 0) { | 1557 // if odd, increment |
1515 return; | 1558 if (b.lastDigit & 1 != 0) b++; |
1516 } | |
1517 // if last digit is odd... | |
1518 if (lastDigit(number.ceff) & 1 == 1) { | |
1519 increment(number.ceff); | |
1520 }+/ | |
1521 return b; | 1559 return b; |
1522 | 1560 |
1523 case Rounding.CEILING: | 1561 case Rounding.CEILING: |
1524 if (!b.isSigned && !r.isZero) b++; | 1562 if (!b.isSigned && !r.isZero) b++; |
1525 return b; | 1563 return b; |
1526 | 1564 |
1527 case Rounding.FLOOR: | 1565 case Rounding.FLOOR: |
1528 if (b.isSigned && !r.isZero) b++; | 1566 if (b.isSigned && !r.isZero) b++; |
1529 return b; | 1567 return b; |
1530 | 1568 |
1531 // TODO: is this one right? what about 55... | |
1532 case Rounding.HALF_DOWN: | 1569 case Rounding.HALF_DOWN: |
1533 if (r.firstDigit > 5) b++; | 1570 if (rfd < 5) return b; |
1571 if (rfd > 5) return ++b; | |
1572 Bcd p = r.dup; | |
1573 stripr(p); | |
1574 if (p.numDigits > 1) return ++b; | |
1575 // to reach this point the remainder must be exactly 5 | |
1534 return b; | 1576 return b; |
1535 | 1577 |
1536 case Rounding.UP: | 1578 case Rounding.UP: |
1537 if (!r.isZero) b++; | 1579 if (!r.isZero) b++; |
1538 return b; | 1580 return b; |
1539 } | 1581 } |
1540 return b; | 1582 return b; |
1541 } | 1583 } |
1542 | 1584 |
1543 unittest { | 1585 unittest { |
1544 write("rounding......."); | 1586 write("rounding...."); |
1587 Bcd a, b; | |
1588 a = 12345; | |
1589 b = round(a, 4, Rounding.DOWN); | |
1590 assert(b == 1234); | |
1591 b = round(a, 4, Rounding.HALF_UP); | |
1592 assert(b == 1235); | |
1593 b = round(a, 4, Rounding.CEILING); | |
1594 assert(b == 1235); | |
1595 b = round(a, 4, Rounding.HALF_EVEN); | |
1596 assert(b == 1234); | |
1597 b = round(a, 4, Rounding.FLOOR); | |
1598 assert(b == 1234); | |
1599 b = round(a, 4, Rounding.HALF_DOWN); | |
1600 assert(b == 1234); | |
1601 b = round(a, 4, Rounding.UP); | |
1602 assert(b == 1235); | |
1545 writeln("passed"); | 1603 writeln("passed"); |
1546 } | 1604 } |
1547 | 1605 |
1548 | 1606 |
1549 //========================================== | 1607 //========================================== |