comparison src/decimal/bcd.d @ 10:f925fe996255

added cast, clip, power
author Paul (paul.d.anderson@comcast.net)
date Thu, 25 Mar 2010 20:05:00 -0700
parents 48d564218e05
children 8e5f172ec55c
comparison
equal deleted inserted replaced
9:48d564218e05 10:f925fe996255
30 * DEALINGS IN THE SOFTWARE. 30 * DEALINGS IN THE SOFTWARE.
31 **/ 31 **/
32 32
33 module decimal.bcd; 33 module decimal.bcd;
34 34
35 //import decimal.context: RoundingMode;
36 import std.algorithm: max; 35 import std.algorithm: max;
37 import std.conv: ConvError; 36 import std.conv: ConvError;
38 import std.math; 37 import std.math;
39 import std.stdio: write, writeln; 38 import std.stdio: write, writeln;
40 import std.string: strip; 39 import std.string: strip;
42 alias ubyte Digit; 41 alias ubyte Digit;
43 42
44 public immutable Bcd ZERO = { digits:[0] }; 43 public immutable Bcd ZERO = { digits:[0] };
45 public immutable Bcd ONE = { digits:[1] }; 44 public immutable Bcd ONE = { digits:[1] };
46 public immutable Bcd NEG_ONE = { sign:true, digits:[1] }; 45 public immutable Bcd NEG_ONE = { sign:true, digits:[1] };
46
47 /+static this() {
48 immutable(Bcd) MAX_LONG = cast(immutable) Bcd(long.max);
49 // MAX_LONG = cast(immutable) Bcd(long.max);
50 }+/
47 51
48 /** 52 /**
49 * Enumeration of available rounding modes. 53 * Enumeration of available rounding modes.
50 */ 54 */
51 public enum Rounding { 55 public enum Rounding {
216 * padding or truncating if necessary. 220 * padding or truncating if necessary.
217 * If truncating, first leading zeros are stripped away, then 221 * If truncating, first leading zeros are stripped away, then
218 * the remainder is clipped. 222 * the remainder is clipped.
219 */ 223 */
220 void setNumDigits(uint n) { 224 void setNumDigits(uint n) {
225 if (n == 0) n++;
226 digits.length = n;
227 }
228
229 /+ /**
230 * Adjusts the number of digits in this BCD integer,
231 * padding or truncating if necessary.
232 * If truncating, first leading zeros are stripped away, then
233 * the remainder is clipped.
234 */
235 void setNumDigits(uint n) {
221 stripl(this); 236 stripl(this);
222 if (n > digits.length) { 237 if (n > digits.length) {
223 digits.length = n; 238 digits.length = n;
224 } 239 }
225 else { 240 else {
226 this = truncate(this, n); 241 this = truncate(this, n);
227 } 242 }
228 } 243 }+/
229 244
230 unittest { 245 /+unittest {
231 write("setWidth..."); 246 write("setWidth...");
232 Bcd a; 247 Bcd a;
233 a = 1234567; 248 a = 1234567;
234 a.setNumDigits(5); 249 a.setNumDigits(5);
235 assert(a == Bcd(12345)); 250 assert(a == Bcd(12345));
236 a.setNumDigits(9); 251 a.setNumDigits(9);
237 assert(a.toString == "000012345"); 252 assert(a.toString == "000012345");
238 writeln("passed"); 253 writeln("passed");
239 } 254 }+/
240 255
241 /** 256 /**
242 * Returns the first digit of this BCD integer. If the 257 * Returns the first digit of this BCD integer. If the
243 * integer has leading zeros this function will return 258 * integer has leading zeros this function will return
244 * zero, but the value of the number is not necessarily zero. 259 * zero, but the value of the number is not necessarily zero.
269 void setDigit(uint n, Digit value) { 284 void setDigit(uint n, Digit value) {
270 assert(value < 10); 285 assert(value < 10);
271 digits[$-n-1] = value; 286 digits[$-n-1] = value;
272 } 287 }
273 288
274 unittest { 289 /+ unittest {
275 write("digits..."); 290 write("digits...");
276 Bcd bcd; 291 Bcd bcd;
277 bcd = 12345678; 292 bcd = 12345678;
278 assert(bcd.numDigits() == 8); 293 assert(bcd.numDigits() == 8);
279 assert(bcd.firstDigit() == 1); 294 assert(bcd.firstDigit() == 1);
286 assert(bcd.firstDigit() == 0); 301 assert(bcd.firstDigit() == 0);
287 assert(bcd.getDigit(5) == 7); 302 assert(bcd.getDigit(5) == 7);
288 bcd.setNumDigits(5); 303 bcd.setNumDigits(5);
289 assert(bcd.getDigit(2) == 3); 304 assert(bcd.getDigit(2) == 3);
290 writeln("passed"); 305 writeln("passed");
291 } 306 }+/
292 307
293 //-------------------------------- 308 //--------------------------------
294 // Common object functions. 309 // Common object functions.
295 //-------------------------------- 310 //--------------------------------
296 311
378 b = "-00000000"; 393 b = "-00000000";
379 assert(b.isZero); 394 assert(b.isZero);
380 writeln("passed"); 395 writeln("passed");
381 } 396 }
382 397
398 //--------------------------------
399 // casting operators.
400 //--------------------------------
401
402 const long opCast() {
403 Bcd MAX_LONG = Bcd(long.max);
404 Bcd MIN_LONG = Bcd(long.min);
405
406 if (this > MAX_LONG || this < MIN_LONG)
407 throw new Exception("Can't cast -- out of range");
408
409 long n = 0;
410 foreach_reverse(Digit digit; digits) {
411 n = 10*n + digit;
412 }
413 return this.sign ? -n : n;
414 }
415
416 unittest {
417 write("cast(long)...");
418 Bcd a;
419 a = "12345678901234567890123456";
420 // long n = cast(long) a;
421 a = 12345678L;
422 long n = cast(long) a;
423 assert(n == 12345678L);
424 int m = cast(int) a;
425 assert(m == 12345678L);
426 a = -a;
427 m = cast(int) a;
428 assert(m == -12345678L);
429
430 writeln("passed");
431 }
432
433
383 //-------------------------------- 434 //--------------------------------
384 // Assignment operators. 435 // Assignment operators.
385 //-------------------------------- 436 //--------------------------------
386 437
387 /** 438 /**
612 unittest { 663 unittest {
613 write("compare..."); 664 write("compare...");
614 Bcd a,b; 665 Bcd a,b;
615 a = 100; 666 a = 100;
616 b = 5; 667 b = 5;
668 assert(a > b);
669 a = -100;
670 b = 5;
671 assert(a < b);
672 a = -100;
673 b = -5;
674 assert(a < b);
675 a = 100;
676 b = -5;
617 assert(a > b); 677 assert(a > b);
618 a = 5; 678 a = 5;
619 b = 100; 679 b = 100;
620 assert(a < b); 680 assert(a < b);
621 assert(b > a); 681 assert(b > a);
663 723
664 public bool isCanonical(const Bcd a) { 724 public bool isCanonical(const Bcd a) {
665 // no leading zeros 725 // no leading zeros
666 if (a.hasLeadingZeros) return false; 726 if (a.hasLeadingZeros) return false;
667 // not -0 727 // not -0
668 if (a.numDigits == 1 && a.firstDigit == 0) return !a.sign; 728 /+ if (a.numDigits == 1 && a.firstDigit == 0) return !a.sign;+/
669 return true; 729 return true;
670 } 730 }
671 731
672 // Strips leading zeros and changes sign if == -0 732 // Strips leading zeros and changes sign if == -0
673 public Bcd canonical(const Bcd a) { 733 public Bcd canonical(const Bcd a) {
674 Bcd d = a.dup; 734 Bcd d = a.dup;
675 if (isCanonical(a)) return d; 735 if (isCanonical(a)) return d;
676 stripl(d); 736 stripl(d);
677 if (d.numDigits == 1 && d.firstDigit == 0) { 737 /+ if (d.numDigits == 1 && d.firstDigit == 0) {
678 d.sign = false; 738 d.sign = false;
679 } 739 }+/
680 return d; 740 return d;
681 } 741 }
742
743
744 private void clipFirst(ref Bcd a, uint n = 1) {
745 if (n == 0) return;
746 if (n >= a.digits.length) a = ZERO;
747 else a.digits = a.digits[0..$-n];
748 }
749
750 private void clipLast(ref Bcd a, uint n = 1) {
751 if (n == 0) return;
752 if (n >= a.digits.length) a = ZERO;
753 else a.digits = a.digits[n..$];
754 }
755
756 unittest {
757 write("clipping....");
758 Bcd a;
759 a = 12345;
760 clipFirst(a);
761 assert(a == 2345);
762 clipLast(a);
763 assert(a == 234);
764 a = 1234567890;
765 clipFirst(a, 3);
766 assert(a == 4567890);
767 clipLast(a, 5);
768 assert(a == 45);
769 clipLast(a, 5);
770 assert(a == 0);
771 writeln("passed");
772 }
773
682 774
683 /** 775 /**
684 * Strips leading zeros from the specified decint. 776 * Strips leading zeros from the specified decint.
685 */ 777 */
686 private void stripl(ref Bcd a) { 778 private void stripl(ref Bcd a) {
824 public int compare(const Bcd m, const Bcd n) { 916 public int compare(const Bcd m, const Bcd n) {
825 Bcd a = canonical(m); 917 Bcd a = canonical(m);
826 Bcd b = canonical(n); 918 Bcd b = canonical(n);
827 if (!a.isSigned && b.isSigned) return 1; 919 if (!a.isSigned && b.isSigned) return 1;
828 if (a.isSigned && !b.isSigned) return -1; 920 if (a.isSigned && !b.isSigned) return -1;
829 if (a.digits.length > b.digits.length) return 1; 921 if (!a.isSigned) {
830 if (a.digits.length < b.digits.length) return -1; 922 if (a.digits.length > b.digits.length) return 1;
831 foreach_reverse(int i, Digit digit; a.digits) { 923 if (a.digits.length < b.digits.length) return -1;
832 if (digit > b.digits[i]) return 1; 924 foreach_reverse(int i, Digit digit; a.digits) {
833 if (digit < b.digits[i]) return -1; 925 if (digit > b.digits[i]) return 1;
926 if (digit < b.digits[i]) return -1;
927 }
928 }
929 else {
930 if (a.digits.length > b.digits.length) return -1;
931 if (a.digits.length < b.digits.length) return 11;
932 foreach_reverse(int i, Digit digit; a.digits) {
933 if (digit > b.digits[i]) return -1;
934 if (digit < b.digits[i]) return 1;
935 }
834 } 936 }
835 return 0; 937 return 0;
836 } 938 }
837 939
838 unittest { 940 unittest {
849 bcd = canonical(bcd); 951 bcd = canonical(bcd);
850 // writeln("bcd = ", bcd); 952 // writeln("bcd = ", bcd);
851 writeln("passed"); 953 writeln("passed");
852 } 954 }
853 955
854 public bool sameLength(const Bcd a, const Bcd b) { 956 private bool sameLength(const Bcd a, const Bcd b) {
855 return a.numDigits == b.numDigits; 957 return a.numDigits == b.numDigits;
856 } 958 }
857 959
858 public int setSameLength(ref Bcd a, ref Bcd b) { 960 private int setSameLength(ref Bcd a, ref Bcd b) {
859 if (sameLength(a, b)) return a.numDigits; 961 if (sameLength(a, b)) return a.numDigits;
860 uint alen = a.numDigits; 962 uint alen = a.numDigits;
861 uint blen = b.numDigits; 963 uint blen = b.numDigits;
862 if (alen > blen) { 964 if (alen > blen) {
863 b.setNumDigits(alen); 965 b.setNumDigits(alen);
866 a.setNumDigits(blen); 968 a.setNumDigits(blen);
867 } 969 }
868 return a.numDigits(); 970 return a.numDigits();
869 } 971 }
870 972
871 public Bcd twosComp(const Bcd a) { 973 // TODO: really? private?
974 private Bcd twosComp(const Bcd a) {
872 Bcd b = Bcd(0, a.numDigits); 975 Bcd b = Bcd(0, a.numDigits);
873 foreach(int i, Digit digit; a.digits) { 976 foreach(int i, Digit digit; a.digits) {
874 b.digits[i] = digit == 0 ? 1 : 0 ; 977 b.digits[i] = digit == 0 ? 1 : 0 ;
875 } 978 }
876 return b; 979 return b;
891 public int sgn(const Bcd bcd) { 994 public int sgn(const Bcd bcd) {
892 if (bcd.isZero) return 0; 995 if (bcd.isZero) return 0;
893 return bcd.isSigned ? -1 : 1; 996 return bcd.isSigned ? -1 : 1;
894 } 997 }
895 998
896 public Bcd not(const Bcd x) { 999 public Bcd not(const Bcd a) {
897 Bcd result = cast(Bcd) x; 1000 Bcd b = a.dup;
898 for (int i = 0; i < x.numDigits; i++) { 1001 foreach(Digit digit; b.digits) {
899 result[i] = not(result[i]); 1002 digit = not(digit);
900 } 1003 }
901 return result; 1004 return b;
902 } 1005 }
903 1006
904 private Digit not(const Digit a) { 1007 private Digit not(const Digit a) {
905 return a != 0; 1008 return a != 0;
906 } 1009 }
1307 writeln("passed"); 1410 writeln("passed");
1308 } 1411 }
1309 1412
1310 1413
1311 public Bcd truncate(const Bcd a, int n) { 1414 public Bcd truncate(const Bcd a, int n) {
1312 Bcd dummy; 1415 return truncate(a, n, Bcd());
1313 return truncate(a, n, dummy);
1314 } 1416 }
1315 1417
1316 unittest { 1418 unittest {
1317 write("truncate...."); 1419 write("truncate....");
1318 Bcd a; 1420 Bcd a;
1414 assert(a == "21345000000000000"); 1516 assert(a == "21345000000000000");
1415 a = pow10(9); 1517 a = pow10(9);
1416 assert(a == Bcd("1000000000")); 1518 assert(a == Bcd("1000000000"));
1417 writeln("passed"); 1519 writeln("passed");
1418 } 1520 }
1521
1522 public Bcd power(const Bcd a, uint n) {
1523 Bcd b = canonical(a);
1524 Bcd p = ONE.dup;
1525 while (n > 0) {
1526 if (n & 1) {
1527 p *= b;
1528 if (n == 1) return p;
1529 }
1530 b *= b;
1531 n /= 2;
1532 }
1533 return p;
1534 }
1535
1536 unittest {
1537 write("power.......");
1538 Bcd a;
1539 a = 2;
1540 int n = 3;
1541 assert(power(a,n) == 8);
1542 writeln("passed");
1543 }
1544
1419 1545
1420 public Bcd shift(const Bcd a, int n) { 1546 public Bcd shift(const Bcd a, int n) {
1421 if (n == 0) return canonical(a); 1547 if (n == 0) return canonical(a);
1422 if (n > 0) { // shift left 1548 if (n > 0) { // shift left
1423 return pow10(a, n); 1549 return pow10(a, n);