# HG changeset patch # User Paul (paul.d.anderson@comcast.net) # Date 1269314405 25200 # Node ID 14badf9104b929a4eff64c1a16c5def35a2cebe7 # Parent c021ed211f892c81560f815a95de59aa26173544 shifts, rotations, division diff -r c021ed211f89 -r 14badf9104b9 src/decimal/bcd.d --- a/src/decimal/bcd.d Sat Mar 20 21:38:22 2010 -0700 +++ b/src/decimal/bcd.d Mon Mar 22 20:20:05 2010 -0700 @@ -32,6 +32,7 @@ module decimal.bcd; +//import decimal.context: RoundingMode; import std.algorithm: max; import std.conv: ConvError; import std.math; @@ -45,6 +46,19 @@ public immutable Bcd NEG_ONE = { sign:true, digits:[1] }; /** + * Enumeration of available rounding modes. + */ +public enum Rounding { + HALF_EVEN, + HALF_DOWN, + HALF_UP, + DOWN, + UP, + FLOOR, + CEILING, +} + +/** * Provides BCD-encoded integral values of arbitrary length. * * Advantages of BCD: @@ -205,6 +219,19 @@ digits.length = n; } +unittest { + write("setWidth..."); + Bcd a; + a = 1234567; + a.setNumDigits(5); + writeln("a = ", a); + assert(a == Bcd(34567)); + a.setNumDigits(9); + writeln("a = ", a); + assert(a.toString == "000034567"); + writeln("passed"); +} + /** * Returns the first digit of this BCD integer. If the * integer has leading zeros this function will return @@ -432,11 +459,31 @@ } Bcd opPostInc() { - return this + ONE; + this = this + ONE; + return this; } Bcd opPostDec() { - return this - ONE; + this = this - ONE; + return this; + } + + const Bcd opShl(int n) { + return shift(this, n); + } + + Bcd opShlAssign(int n) { + this = this << n; + return this; + } + + const Bcd opShr(int n) { + return shift(this, -n); + } + + Bcd opShrAssign(int n) { + this = this >> n; + return this; } //-------------------------------- @@ -829,6 +876,7 @@ return (a == 0 && b != 0) || (a != 0 && b == 0); } +// TODO: watch the leading zeros public Bcd add(const Bcd x, const Bcd y) { Bcd a = x.dup; Bcd b = y.dup; @@ -862,7 +910,7 @@ } sum.sign = sign; } - return sum; + return stripLeadingZeros(sum); } unittest { @@ -1080,9 +1128,163 @@ writeln("passed"); } +public Bcd truncate(const Bcd a, int n) { +/+ if (n <= 0) return ZERO.dup; + if (n >= a.numDigits) return a.dup; + Bcd b; + b.digits = a.digits[$-n..$].dup; + b.sign = a.sign; + return b;+/ + Bcd dummy; + return truncate(a, n, dummy); +} + +unittest { + write("truncate...."); + Bcd a; + a = 1234567; + assert(truncate(a,3) == 123); + a = 10256; + assert(truncate(a,5) == 10256); + a = 8500; + assert(truncate(a,1) == 8); + a = -8500; + assert(truncate(a,1) == -8); + a = -8500; + assert(truncate(a,-1) == 0); + writeln("passed"); +} + +/** + * Truncate to the specified number of digits + */ +public Bcd truncate(const Bcd a, int n, out Bcd r) { + if (n <= 0) { + r = stripLeadingZeros(a); + return ZERO.dup; + } + if (n >= a.numDigits) { + r = ZERO.dup; + return stripLeadingZeros(a); + } + Bcd b; + b.digits = a.digits[$-n..$].dup; + b.sign = a.sign; + r.digits = a.digits[0..$-n].dup; + return b; +} + +unittest { + write("truncRem..."); + Bcd a; + Bcd r; + a = 1234567; + assert(truncate(a, 3, r) == 123); + assert(r == 4567); + a = 10256; + assert(truncate(a,5,r) == 10256); + assert(r == 0); + a = 8500; + assert(truncate(a,1,r) == 8); + assert(r == 500); + a = -8500; + assert(truncate(a,1,r) == -8); + assert(r == 500); + a = -8500; +// writeln("truncate(a, 6, r) = ", truncate(a, 6, r)); +// writeln("r = ", r); + assert(truncate(a,6,r) == -8500); + assert(r == 0); + writeln("passed"); +} + +public Bcd pad(Bcd a, int n) { + if (n <= a.numDigits) return a.dup; + return Bcd(a,n); +} + +unittest { + write("pad........."); + Bcd a; + a = 1234567; + assert(pad(a,3) == 1234567); + a = 10256; +// writeln("pad(a,9) = ", pad(a,9)); + assert(pad(a,9).toString == "000010256"); + a = 8500; + assert(pad(a,6).toString == "008500"); + a = -8500; + assert(pad(a,6).toString == "-008500"); + writeln("passed"); +} +public Bcd divide(const Bcd a, const Bcd b, out Bcd remainder) { + Bcd quotient; + Bcd dividend = stripLeadingZeros(a); +// writeln("dividend = ", dividend); + Bcd divisor = stripLeadingZeros(b); +// writeln("divisor = ", divisor); + if (divisor == ZERO) throw new Exception("Divide by zero"); + remainder = dividend; + writeln("remainder = ", remainder); + Bcd next = divisor; +// writeln("next = ", next); + Bcd multiple; + + do { + multiple = next; +// writeln("multiple = ", multiple); + next <<= 1; +// writeln("next = ", next); + } while (next <= remainder && next > multiple); + + while (multiple >= divisor) { + quotient <<= 1; +// writeln("quotient = ", quotient); + while (multiple <= remainder) { + remainder -= multiple; + writeln("remainder = ", remainder); + quotient++; +// writeln("quotient = ", quotient); + } + multiple >>= 1; +// writeln("multiple = ", multiple); + } + return quotient; +} + +unittest { + write("divide..."); + writeln(); + Bcd a, b, q, r; + a = 144; + b = 12; + q = divide(a, b, r); + assert(q == 12); + assert(r == 0); + a = 144; + b = 14; + q = divide(a, b, r); + assert(q == 10); + assert(r == 4); + a = 142351; + b = 12; + q = divide(a, b, r); + writeln("q = ", q); + writeln("r = ", r); + assert(q == 11862); + assert(r == 7); + a = 2; + b = 12; + q = divide(a, b, r); + assert(q == 0); + assert(r == 2); + writeln("passed"); +} + + public Bcd pow10(const Bcd a, uint n) { - Bcd b = a.dup; - if (n == 0) return b; + Bcd b = stripLeadingZeros(a); + if (n == 0 || b == 0) return b; Digit[] zeros = new Digit[n]; b.digits = zeros ~ b.digits; return b; @@ -1106,13 +1308,45 @@ } public Bcd shift(const Bcd a, int n) { + if (n == 0) return stripLeadingZeros(a); + if (n > 0) { // shift left + return pow10(a, n); + } + else { // shift right + if (-n >= a.numDigits) return ZERO.dup; + return truncate(a, a.numDigits + n); + } +} + +unittest { + write("shift......."); + Bcd a, b; + a = 1234; + b = shift(a, 1); + assert(b.toString == "12340"); + b = shift(a, 2); + assert(b.toString == "123400"); + b = shift(a, -1); +// writeln("b = ", b); + assert(b.toString == "123"); + b = shift(a, 0); + assert(b.toString == "1234"); + b = shift(a, -12); +/+ writeln("b = ", b);+/ + assert(b.toString == "0"); + b = shift(a, -4); + assert(b.toString == "0"); + writeln("passed"); +} + +public Bcd shiftFixed(const Bcd a, int n) { if (n == 0) return a.dup; Bcd b = Bcd(0, a.numDigits); if (std.math.abs(n) >= a.numDigits) return b; - if (n > 0) { // shift left + if (n > 0) { // shiftFixed left b.digits[0..$-n] = a.digits[n..$]; } - else { // shift right + else { // shiftFixed right n = -n; b.digits[n..$] = a.digits[0..$-n]; } @@ -1120,20 +1354,20 @@ } unittest { - write("shift..."); + write("shiftFixed..."); Bcd a, b; a = 1234; - b = shift(a, 1); + b = shiftFixed(a, 1); assert(b.toString == "0123"); - b = shift(a, 2); + b = shiftFixed(a, 2); assert(b.toString == "0012"); - b = shift(a, -1); + b = shiftFixed(a, -1); assert(b.toString == "2340"); - b = shift(a, 0); + b = shiftFixed(a, 0); assert(b.toString == "1234"); - b = shift(a, 12); + b = shiftFixed(a, 12); assert(b.toString == "0000"); - b = shift(a, -4); + b = shiftFixed(a, -4); assert(b.toString == "0000"); writeln("passed"); }