Mercurial > projects > decimal
view src/decimal/bcd.d @ 4:b37c218c1442
Initial development of BCD integers
author | Paul (paul.d.anderson@comcast.net) |
---|---|
date | Thu, 18 Mar 2010 18:10:25 -0700 |
parents | |
children | c021ed211f89 |
line wrap: on
line source
/** * 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!"); }