Mercurial > projects > decimal
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); |