# HG changeset patch # User Paul (paul.d.anderson@comcast.net) # Date 1268961025 25200 # Node ID b37c218c1442d49f049e7e4c5a64e7cbcc0ad974 # Parent bfda0b347c07726b1c9ec4650975854aadb02ba3 Initial development of BCD integers diff -r bfda0b347c07 -r b37c218c1442 src/decimal/bcd.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/decimal/bcd.d Thu Mar 18 18:10:25 2010 -0700 @@ -0,0 +1,602 @@ +/** + * A D programming language implementation of the + * General Decimal Arithmetic Specification, + * Version 1.70, (25 March 2009). + * + * by Paul D. Anderson + * + * Boost Software License - Version 1.0 - August 17th, 2003 + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare derivative works of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. +**/ + +module decimal.bcd; + +import std.conv: ConvError; +import std.math; +import std.stdio: write, writeln; +import std.string: strip; + +alias ubyte Digit; + +public static immutable Bcd ZERO = { digits:[0] }; +public static immutable Bcd ONE = { digits:[1] }; +public static immutable Bcd NEG_ONE = { sign:true, digits:[1] }; + +public struct Bcd { + + // members + private bool sign = false; + private Digit[] digits = [ 0 ]; + + // constructors + public this(const Bcd bcd) { + this = bcd; + } + + public this(const Bcd bcd, uint numDigits) { + this = bcd; + setNumDigits(numDigits); + } + + public this(const string str) { + this = str; + } + + public this(const long n) { + this = n; + } + + public this(const long n, uint numDigits) { + this = n; + setNumDigits(numDigits); + } + + const string toString() { + return format(this); + } + + const hash_t toHash() { + hash_t hash = 0; + foreach(Digit digit; digits) { + hash += digit; + } + return hash; + } + + // access methods + const uint numDigits() { + return digits.length; + } + + void setNumDigits(uint n) { + digits.length = n; + } + + const uint firstDigit() { + return digits[$-1]; + } + + const uint lastDigit() { + return digits[0]; + } + + void setDigit(uint n, Digit value) { + digits[$-n-1] = value; + } + + const int getDigit(int n) { + return digits[$-n-1]; + } + + unittest { + write("digits..."); + Bcd bcd = Bcd(12345678L); + assert(bcd.numDigits() == 8); + assert(bcd.firstDigit() == 1); + assert(bcd.lastDigit() == 8); + assert(bcd.getDigit(3) == 4); + bcd.setDigit(3, 7); + assert(bcd.getDigit(3) == 7); + bcd.setNumDigits(10); + assert(bcd.numDigits == 10); + assert(bcd.firstDigit() == 0); + assert(bcd.getDigit(5) == 7); + bcd.setNumDigits(5); + assert(bcd.getDigit(2) == 6); +// writeln("bcd = ", bcd); + writeln("passed!"); + } + + const bool isSigned() { + return sign; + } + + const bool isZero() { + writeln("stripping"); + Bcd temp = stripLeadingZeros(this); + writeln("stripped"); + writeln("temp = ", temp); + if (temp.numDigits > 1) return false; + return temp.lastDigit == 0; + } + + const bool hasLeadingZeros() { + return numDigits > 0 && firstDigit == 0; + } + + void opAssign(const Bcd that) { + this.sign = that.sign; + this.digits.length = that.digits.length; + this.digits[] = that.digits[]; + } + + void opAssign(const string str) { + this = parse(str); + } + + void opAssign(const long n) { + uint m; + if (n < 0) { + sign = true; + m = std.math.abs(n); + } + else { + sign = false; + m = n; + } + Digit[20] dig; + int i = 0; + do { + uint q = m/10; + uint r = m%10; + dig[i] = cast(Digit) r; + m = q; + i++; + } while (m > 0); + digits = dig[0..i].dup; + } + + + // index operator + Digit opIndex(uint index) { + return this.digits[index]; + } + + // index assign operator + void opIndexAssign(Digit value, uint index) { + this.digits[index] = value; + } + +/+ int opApply(int delegate (ref Digit) dg) { + int result = 0; + for (int i = 0; i < numDigits; i++) { + result = dg(digits[i]); + if (result) break; + } + return result; + } + + int opApply(int delegate (ref Digit) dg) { + int result = 0; + for (int i = 0; i < numDigits; i++) { + result = dg(digits[i]); + if (result) break; + } + return result; + } + + int opApplyReverse(int delegate (ref Digit) dg) { + int result = 0; + for (int i = 0; i < numDigits; i++) { + result = dg(digits[i]); + if (result) break; + } + return result; + }+/ + + const Bcd dup() { + Bcd bcd; + bcd.sign = this.sign; + bcd.digits.length = this.digits.length; + bcd.digits[] = this.digits[]; + return bcd; + } + + // TODO: always uncertain how this is done...will it make an unnecessary copy? + const Bcd opPos() { + return this.dup; + } + + const Bcd opNeg() { + return negate(this); + } + + const Bcd opCom() { + return complement(this); + } + + const Bcd opNot() { + return not(this); + } + + const Bcd opAnd(const Bcd bcd) { + return and(this, bcd); + } + + const Bcd opOr(const Bcd bcd) { + return or(this, bcd); + } + + const Bcd opXor(const Bcd bcd) { + return xor(this, bcd); + } + + // TODO: what's the matter with the one-digit numbers?? + const bool opEquals(T:Bcd)(const T that) { +// writeln("equating"); + if (this.sign != that.sign) return false; // what about +/- zero? +// writeln("same signs"); + Bcd thisOne = stripLeadingZeros(this); +// writeln("this = ", thisOne); + Bcd thatOne = stripLeadingZeros(that); +// writeln("that = ", thatOne); + if (thisOne.digits.length != thatOne.digits.length) return false; +// writeln("length = ", thisOne.digits.length); +// foreach(int i, Digit digit; thisOne) { // todo: have to make opApply here. +// } + foreach_reverse(int i, Digit digit; thisOne.digits) { +/+ writeln("i = ", i); + writeln("this[i] = ", thisOne.digits[i]); + writeln("that[i] = ", thatOne.digits[i]); + writeln("this = ", thisOne.digits); + writeln("that = ", thatOne.digits);+/ + if (digit != thatOne.digits[i]) return false; + } + return true; + } + + const bool opEquals(T)(const T that) { + return opEquals(Bcd(that)); + } + +} // end struct Bcd + +private bool isDigit(const char ch) { + return (ch >= '0' && ch <= '9'); +} + +public string format(const Bcd bcd, bool showPlus = false, bool showLeadingZeros = false) { + int len = bcd.digits.length; + if (bcd.sign) len++; + char[] str = new char[len]; + + int index = 0; + if (bcd.sign) { + str[index] = '-'; + index++; + } + + foreach_reverse(Digit digit; bcd.digits) { + str[index] = cast(char) digit + '0'; + index++; + } + return str.idup; +} + +public Bcd parse(string str) { + Bcd bcd; + // are leading zeros retained? + bool lz = false; + + // strip whitespace, convert to char array + char[] str1 = str.strip.dup; + + // check for leading '-' + if (str1[0] == '-') { + bcd.sign = true; + str1 = str1[1..$]; + lz = true; + } + + // check for leading '+' + if (str1[0] == '+') { + bcd.sign = false; + str1 = str1[1..$]; + lz = true; + } + + // verify chars are digits, strip out underscores + int index = 0; + char[] str2 = new char[str1.length]; + foreach_reverse(char ch; str1) { + if (isDigit(ch)) { + str2[index] = ch; + index++; + continue; + } + if (ch == '_') { + continue; + } + throw (new ConvError("Invalid character: " ~ ch)); + } + str2.length = index; + + bcd.digits.length = index; + foreach(int i, char ch; str2) { + bcd.digits[i] = cast(Digit)(ch - '0'); + } + + if (!lz) return stripLeadingZeros(bcd); + return bcd; +} + +public Bcd copy(const Bcd bcd) { + return bcd.dup; +} + +public Bcd negate(const Bcd bcd) { + Bcd copy = bcd; //.dup; + copy.sign = !bcd.sign; + return copy; +} + +public Bcd abs(const Bcd bcd) { + return bcd.isSigned ? -bcd: +bcd; +} + +public Bcd stripLeadingZeros(const Bcd bcd) { + Bcd result = bcd.dup; + if (!bcd.hasLeadingZeros) return result; + int len = bcd.numDigits; + int i = 0; + while(i < len-1 && bcd.getDigit(i) == 0) { + i++; + } + result.setNumDigits(len - i); + return result; +} + + +unittest { + write("strip..."); + Bcd bcd; + bcd = "+00123"; + assert(bcd.toString == "00123"); + bcd = stripLeadingZeros(bcd); + writeln("bcd = ", bcd); + assert(bcd.toString == "123"); + bcd = "+000"; + writeln("bcd = ", bcd); + assert(bcd.toString == "000"); + bcd = stripLeadingZeros(bcd); + writeln("bcd = ", bcd); + writeln("passed"); +} + +public bool sameLength(const Bcd a, const Bcd b) { + return a.numDigits == b.numDigits; +} + +public int setSameLength(ref Bcd a, ref Bcd b) { + if (sameLength(a,b)) return a.numDigits; + uint alen = a.numDigits; + uint blen = b.numDigits; + if (alen > blen) { + b.setNumDigits(alen); + } + else { + a.setNumDigits(blen); + } + return a.numDigits(); +} + +public Bcd complement(const Bcd a) { + Bcd b = Bcd(0, a.numDigits); + foreach(int i, Digit digit; a.digits) { + b.digits[i] = digit == 0 ? 1 : 0 ; + } + return b; +} + +unittest { + write("com..."); + Bcd bcd; + bcd = 123; + assert(complement(bcd).toString == "000"); + bcd = 40509; + assert(complement(bcd).toString == "01010"); +// writeln("bcd = ", bcd); +// writeln("~bcd = ", complement(bcd)); + writeln("passed"); +} + +public int sgn(Bcd bcd) { + if (bcd.isZero) return 0; + return bcd.isSigned ? -1 : 1; +} + +public Bcd not(const Bcd x) { + Bcd result = x.dup; + for (int i = 0; i < x.numDigits; i++) { + result[i] = not(result[i]); + } + return result; +} + +private Digit not(const Digit a) { + return a != 0; +} + +public Bcd and(const Bcd x, const Bcd y) { + Bcd a = x.dup; + Bcd b = y.dup; + int len = setSameLength(a,b); + Bcd result = Bcd(0, len); + for (int i = 0; i < len; i++) { + result[i] = and(a[i], b[i]); + } + return result; +} + +private Digit and(const Digit a, const Digit b) { + return a != 0 && b != 0; +} + +unittest { + write("and..."); + Bcd a; + Bcd b; + a = 123000; + b = 123000; + assert(and(a,b).toString == "111000"); + b = 12300; + assert(and(a,b).toString == "011000"); + a = 1234567; + b = 7654321; + assert(and(a,b).toString == "1111111"); + a = 1010; + b = 101; + writeln("a = ", a); + writeln("b = ", b); + writeln("and = ", and(a,b)); + assert(and(a,b).isZero); + writeln("passed!"); +} + +// TODO: looks like there's some room for templates or mixins here. +public Bcd or(const Bcd x, const Bcd y) { + Bcd a = x.dup; + Bcd b = y.dup; + int len = setSameLength(a,b); + Bcd result = Bcd(0, len); + for (int i = 0; i < len; i++) { + result[i] = or(a[i], b[i]); + } + return result; +} + +private Digit or(const Digit a, const Digit b) { + return a != 0 || b != 0; +} + + +public Bcd xor(const Bcd x, const Bcd y) { + Bcd a = x.dup; + Bcd b = y.dup; + int len = setSameLength(a,b); + Bcd result = Bcd(0, len); + for (int i = 0; i < len; i++) { + result[i] = xor(a[i], b[i]); + } + return result; +} + +private Digit xor(const Digit a, const Digit b) { + return (a == 0 && b != 0) || (a != 0 && b == 0); +} + +public Bcd add(const Bcd a, const Bcd b) { + Bcd x = a.dup; + Bcd y = b.dup; + int len = setSameLength(x, y); + Bcd result = Bcd(0, len+1); + Digit carry = 0; + uint i; + for (i = 0; i < len; i++) { + result[i] = add(x[i], y[i], carry); + } + if (carry) { + result[i] = 1; + } + else { + result = stripLeadingZeros(result); + } + return result; +} + +private Digit add(const Digit a, const Digit b, ref Digit carry) { + Digit sum = a + b + carry; + if (sum > 9) { + sum -= 10; + carry = 1; + } + else { + carry = 0; + } + return sum; +} + +unittest { + write("add..."); + Bcd a, b; + a = 123; + b = 222; + Bcd sum = add(a,b); + assert(sum == 345); + a = 2; + b = 102000; + sum = add(a,b); + assert(sum == 102002); + writeln("passed!"); +} + +//========================================== + +public void main() { + writeln("Hello, world"); + Bcd bcd; + writeln("bcd = ", format(bcd)); + bcd = parse("12"); + writeln("bcd = ", format(bcd)); + bcd = parse("012"); + writeln("bcd = ", format(bcd)); + bcd = parse("-012"); + writeln("bcd = ", format(bcd)); + bcd = parse("+012"); + writeln("bcd = ", format(bcd)); + bcd = parse("-0"); + writeln("bcd = ", format(bcd)); + bcd = parse("012_345_678"); + writeln("bcd = ", format(bcd)); + + bcd = 1234; + writeln("bcd = ", format(bcd)); + bcd = 12345678L; + writeln("bcd = ", format(bcd)); + writeln("a == b : ", Bcd(0) == Bcd(0)); + writeln("a == b : ", Bcd(4) == Bcd(4)); + writeln("a == b : ", Bcd(40) == Bcd(40)); + writeln("a == b : ", Bcd(-400) == Bcd(-400)); + writeln("a == b : ", Bcd(12345678) == Bcd(+12345678)); + writeln("a != b : ", Bcd(40) == Bcd(53)); + writeln("a != b : ", Bcd(402) == Bcd(531)); + writeln("a != b : ", Bcd(402) == Bcd(-402)); + writeln("a != b : ", Bcd(65432) == Bcd(65431)); + writeln("a != b : ", Bcd(2) == Bcd(1)); + writeln("a != b : ", Bcd(1) == Bcd(2)); + writeln("OK!"); +} + + + diff -r bfda0b347c07 -r b37c218c1442 src/decimal/math.d --- a/src/decimal/math.d Thu Mar 18 18:09:20 2010 -0700 +++ b/src/decimal/math.d Thu Mar 18 18:10:25 2010 -0700 @@ -34,6 +34,7 @@ import decimal.decimal; import decimal.context; +//import std.math; public: @@ -42,6 +43,14 @@ // CONSTANTS // //-------------------------------- + +/+ public Decimal HALF; + public Decimal ONE = Decimal(1); + public Decimal ZERO = Decimal(0); + + static { + HALF = Decimal("0.5"); + }+/ /** * Returns the value of e to the default precision. @@ -59,14 +68,62 @@ return result; } +/+ /** * Returns the value of pi to the default precision. */ Decimal pi() { - Decimal result; - return result; + Decimal ONE = Decimal(1); + writeln("1 = ", ONE); + Decimal TWO = Decimal(2); + writeln("2 = ", TWO); + Decimal HALF = Decimal(0.5); + writeln("0.5 = ", HALF); + Decimal x = sqrt(TWO); + writeln("x = ", x); + Decimal y = sqrt(x); + writeln("y = ", y); + Decimal p = TWO + x; + writeln("p = ", p); + x = y; + int i = 0; + while (true) { + writeln("i = ", i); + x = HALF * (x + ONE/x); + writeln("step 1"); + Decimal np = p * ((x + ONE)/(y + ONE)); + writeln("step 2"); + writeln("np = ", np); + if (p == np) return p; + writeln("step 3"); + p = np; + writeln("step 4"); + x = sqrt(x); + writeln("step 5"); +// writeln("x = ", x); + Decimal oox = ONE/x; +// writeln("ONE/x = ", oox); +// writeln ("x + ONE/x = ", x + oox); +// Decimal t1 = oox + x; +// writeln("t1 = ", t1); +// Decimal t1 = x + oox; // ONE + oox; //x + x; // + ONE/x; + writeln("step 6"); +// Decimal t2 = (y * x) + ONE; ///x; //ONE / (y + ONE); +// y = ONE/x + y * x; + writeln("step 7"); + y = (ONE/x + y * x) / (y + ONE); //t1 / t2; + writeln("step 8"); + i++; +// break; + } + return p; } - + + unittest { + write("pi...."); + writeln("pi = ", pi()); + } ++/ /** * Returns the value of pi to the specified precision. */ @@ -76,19 +133,38 @@ } /** - * Returns the square root of the argument to the default precision. + * Returns the square root of the argument to the current precision. */ Decimal sqrt(Decimal arg) { - Decimal result; - return result; + return sqrt(arg, context.precision); } + + + unittest { + write("sqrt....."); + Decimal dcm = Decimal(4); + assert(sqrt(dcm) == Decimal(2)); + writeln("sqrt(2) = ", sqrt(Decimal(2))); + } /** * Returns the square root of the argument to the specified precision. */ Decimal sqrt(Decimal arg, uint precision) { - Decimal result; - return result; + pushContext(); + context.precision = precision + 2; + Decimal HALF = Decimal(0.5); + Decimal ONE = Decimal(1); + Decimal x = HALF * (arg + ONE); + Decimal xp; + while(true){ + xp = x; + x = HALF * (x + arg/x); + if (x == xp) break; + } + popContext(); + round(xp); + return xp; } //-------------------------------- @@ -312,7 +388,7 @@ return result; } - //-------------------------------- +/+ //-------------------------------- // // General Decimal Arithmetic Specification Functions // @@ -553,6 +629,6 @@ Decimal result; return result; } ++/ - diff -r bfda0b347c07 -r b37c218c1442 src/decimal/test.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/decimal/test.d Thu Mar 18 18:10:25 2010 -0700 @@ -0,0 +1,135 @@ +/** + * A D programming language implementation of the + * General Decimal Arithmetic Specification, + * Version 1.70, (25 March 2009). + * + * by Paul D. Anderson + * + * Boost Software License - Version 1.0 - August 17th, 2003 + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare derivative works of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. +**/ +module decimal.test; + +import decimal.decimal; +import decimal.math; + +public void main2() { + Decimal ONE = Decimal(1); + writeln("1 = ", ONE); + Decimal TWO = Decimal(2); + writeln("2 = ", TWO); + Decimal HALF = Decimal(0.5); + writeln("0.5 = ", HALF); + Decimal x = sqrt(TWO); + writeln("x = ", x); + Decimal y = sqrt(x); + writeln("y = ", y); + Decimal p = TWO + x; + writeln("p = ", p); + x = y; + int i = 0; + while (true) { + writeln("i = ", i); + x = x + ONE/x; + writeln("step 0"); +// x = HALF (x + ONE/x); + x *= HALF; + writeln("step 1"); + Decimal np = p * ((x + ONE)/(y + ONE)); + writeln("step 2"); + writeln("np = ", np); + if (p == np) return p; + writeln("step 3"); + p = np; + writeln("step 4"); + x = sqrt(x); + writeln("step 5"); +// writeln("x = ", x); + Decimal oox = ONE/x; +// writeln("ONE/x = ", oox); +// writeln ("x + ONE/x = ", x + oox); +// Decimal t1 = oox + x; +// writeln("t1 = ", t1); +// Decimal t1 = x + oox; // ONE + oox; //x + x; // + ONE/x; + writeln("step 6"); +// Decimal t2 = (y * x) + ONE; ///x; //ONE / (y + ONE); +// y = ONE/x + y * x; + writeln("step 7"); + y = (ONE/x + y * x) / (y + ONE); //t1 / t2; + writeln("step 8"); + i++; +// break; + writeln("i = ", i); + + } + return p; +} + +public void main() { + writeln("Hello, world!"); +/+ writeln("eTiny = ", context.eTiny); + writeln("tiny min = ", Decimal(1, context.eTiny)); + writeln("tiny min = ", Decimal(1, context.eTiny - 1)); + writeln("max = ", context.max()); + writeln("max1 = ", Decimal(999999999, 99)); + writeln("dig = ", context.dig()); + writeln("eps = ", context.epsilon()); + writeln("smallest = ", context.min_normal()*context.epsilon()); + writeln("1/epsilon = ", Decimal(1)/context.epsilon()); + writeln("max * min = ", context.max * context.min_normal); + writeln("mant_dig = ", context.mant_dig); + writeln("min_exp = ", context.min_exp); + writeln("max_exp = ", context.max_exp);+/ + + + // TODO: this next one goes crazy -- shows need for checks on construction + // TODO: turns out this is being converted to a double (or real) and + // then it's really weird. +// writeln("bigger = ", Decimal(999999999999)); +// float f = float.max; + // TODO: convert these to assserts +/+ writeln("f.max = ", f); + writeln("f.min = ", float.min); + writeln("f = ", 2 * float.min); + writeln("d.max = ", Decimal.max()); + writeln("d.min = ", Decimal.min_normal()); + writeln("2 * d.min = ", 2 * Decimal.min_normal()); + writeln("0.1 * d.min = ", 0.1 * Decimal.min_normal());+/ + // TODO: move this to unittesting +/+ Decimal dec = Decimal(PI); + writeln("pi = ", dec ); + dec = Decimal(PI, 19); + writeln("pi = ", dec ); + dec = Decimal(1.3, 25); + writeln("pi = ", dec ); + dec = Decimal(0.1, 25); + writeln("pi = ", dec ); + dec = Decimal(2.0, 25); + writeln("pi = ", dec ); + dec = Decimal(200.5, 25); + writeln("pi = ", dec ); + writeln(double.dig); + writeln(real.dig);+/ +} +