Mercurial > projects > decimal
comparison src/decimal/bcd.d @ 6:14badf9104b9
shifts, rotations, division
author | Paul (paul.d.anderson@comcast.net) |
---|---|
date | Mon, 22 Mar 2010 20:20:05 -0700 |
parents | c021ed211f89 |
children | b304232c476c |
comparison
equal
deleted
inserted
replaced
5:c021ed211f89 | 6:14badf9104b9 |
---|---|
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; | |
35 import std.algorithm: max; | 36 import std.algorithm: max; |
36 import std.conv: ConvError; | 37 import std.conv: ConvError; |
37 import std.math; | 38 import std.math; |
38 import std.stdio: write, writeln; | 39 import std.stdio: write, writeln; |
39 import std.string: strip; | 40 import std.string: strip; |
41 alias ubyte Digit; | 42 alias ubyte Digit; |
42 | 43 |
43 public immutable Bcd ZERO = { digits:[0] }; | 44 public immutable Bcd ZERO = { digits:[0] }; |
44 public immutable Bcd ONE = { digits:[1] }; | 45 public immutable Bcd ONE = { digits:[1] }; |
45 public immutable Bcd NEG_ONE = { sign:true, digits:[1] }; | 46 public immutable Bcd NEG_ONE = { sign:true, digits:[1] }; |
47 | |
48 /** | |
49 * Enumeration of available rounding modes. | |
50 */ | |
51 public enum Rounding { | |
52 HALF_EVEN, | |
53 HALF_DOWN, | |
54 HALF_UP, | |
55 DOWN, | |
56 UP, | |
57 FLOOR, | |
58 CEILING, | |
59 } | |
46 | 60 |
47 /** | 61 /** |
48 * Provides BCD-encoded integral values of arbitrary length. | 62 * Provides BCD-encoded integral values of arbitrary length. |
49 * | 63 * |
50 * Advantages of BCD: | 64 * Advantages of BCD: |
203 */ | 217 */ |
204 void setNumDigits(uint n) { | 218 void setNumDigits(uint n) { |
205 digits.length = n; | 219 digits.length = n; |
206 } | 220 } |
207 | 221 |
222 unittest { | |
223 write("setWidth..."); | |
224 Bcd a; | |
225 a = 1234567; | |
226 a.setNumDigits(5); | |
227 writeln("a = ", a); | |
228 assert(a == Bcd(34567)); | |
229 a.setNumDigits(9); | |
230 writeln("a = ", a); | |
231 assert(a.toString == "000034567"); | |
232 writeln("passed"); | |
233 } | |
234 | |
208 /** | 235 /** |
209 * Returns the first digit of this BCD integer. If the | 236 * Returns the first digit of this BCD integer. If the |
210 * integer has leading zeros this function will return | 237 * integer has leading zeros this function will return |
211 * zero, but the value of the number is not necessarily zero. | 238 * zero, but the value of the number is not necessarily zero. |
212 */ | 239 */ |
430 const Bcd opNot() { | 457 const Bcd opNot() { |
431 return not(this); | 458 return not(this); |
432 } | 459 } |
433 | 460 |
434 Bcd opPostInc() { | 461 Bcd opPostInc() { |
435 return this + ONE; | 462 this = this + ONE; |
463 return this; | |
436 } | 464 } |
437 | 465 |
438 Bcd opPostDec() { | 466 Bcd opPostDec() { |
439 return this - ONE; | 467 this = this - ONE; |
468 return this; | |
469 } | |
470 | |
471 const Bcd opShl(int n) { | |
472 return shift(this, n); | |
473 } | |
474 | |
475 Bcd opShlAssign(int n) { | |
476 this = this << n; | |
477 return this; | |
478 } | |
479 | |
480 const Bcd opShr(int n) { | |
481 return shift(this, -n); | |
482 } | |
483 | |
484 Bcd opShrAssign(int n) { | |
485 this = this >> n; | |
486 return this; | |
440 } | 487 } |
441 | 488 |
442 //-------------------------------- | 489 //-------------------------------- |
443 // binary operators | 490 // binary operators |
444 //-------------------------------- | 491 //-------------------------------- |
827 | 874 |
828 private Digit xor(const Digit a, const Digit b) { | 875 private Digit xor(const Digit a, const Digit b) { |
829 return (a == 0 && b != 0) || (a != 0 && b == 0); | 876 return (a == 0 && b != 0) || (a != 0 && b == 0); |
830 } | 877 } |
831 | 878 |
879 // TODO: watch the leading zeros | |
832 public Bcd add(const Bcd x, const Bcd y) { | 880 public Bcd add(const Bcd x, const Bcd y) { |
833 Bcd a = x.dup; | 881 Bcd a = x.dup; |
834 Bcd b = y.dup; | 882 Bcd b = y.dup; |
835 Bcd sum; | 883 Bcd sum; |
836 if (a.sign == b.sign) { | 884 if (a.sign == b.sign) { |
860 sum = tensComp(sum); | 908 sum = tensComp(sum); |
861 sum = stripLeadingZeros(sum); | 909 sum = stripLeadingZeros(sum); |
862 } | 910 } |
863 sum.sign = sign; | 911 sum.sign = sign; |
864 } | 912 } |
865 return sum; | 913 return stripLeadingZeros(sum); |
866 } | 914 } |
867 | 915 |
868 unittest { | 916 unittest { |
869 write("add...."); | 917 write("add...."); |
870 Bcd a, b; | 918 Bcd a, b; |
1078 assert(d == 2); | 1126 assert(d == 2); |
1079 assert(c == 7); | 1127 assert(c == 7); |
1080 writeln("passed"); | 1128 writeln("passed"); |
1081 } | 1129 } |
1082 | 1130 |
1131 public Bcd truncate(const Bcd a, int n) { | |
1132 /+ if (n <= 0) return ZERO.dup; | |
1133 if (n >= a.numDigits) return a.dup; | |
1134 Bcd b; | |
1135 b.digits = a.digits[$-n..$].dup; | |
1136 b.sign = a.sign; | |
1137 return b;+/ | |
1138 Bcd dummy; | |
1139 return truncate(a, n, dummy); | |
1140 } | |
1141 | |
1142 unittest { | |
1143 write("truncate...."); | |
1144 Bcd a; | |
1145 a = 1234567; | |
1146 assert(truncate(a,3) == 123); | |
1147 a = 10256; | |
1148 assert(truncate(a,5) == 10256); | |
1149 a = 8500; | |
1150 assert(truncate(a,1) == 8); | |
1151 a = -8500; | |
1152 assert(truncate(a,1) == -8); | |
1153 a = -8500; | |
1154 assert(truncate(a,-1) == 0); | |
1155 writeln("passed"); | |
1156 } | |
1157 | |
1158 /** | |
1159 * Truncate to the specified number of digits | |
1160 */ | |
1161 public Bcd truncate(const Bcd a, int n, out Bcd r) { | |
1162 if (n <= 0) { | |
1163 r = stripLeadingZeros(a); | |
1164 return ZERO.dup; | |
1165 } | |
1166 if (n >= a.numDigits) { | |
1167 r = ZERO.dup; | |
1168 return stripLeadingZeros(a); | |
1169 } | |
1170 Bcd b; | |
1171 b.digits = a.digits[$-n..$].dup; | |
1172 b.sign = a.sign; | |
1173 r.digits = a.digits[0..$-n].dup; | |
1174 return b; | |
1175 } | |
1176 | |
1177 unittest { | |
1178 write("truncRem..."); | |
1179 Bcd a; | |
1180 Bcd r; | |
1181 a = 1234567; | |
1182 assert(truncate(a, 3, r) == 123); | |
1183 assert(r == 4567); | |
1184 a = 10256; | |
1185 assert(truncate(a,5,r) == 10256); | |
1186 assert(r == 0); | |
1187 a = 8500; | |
1188 assert(truncate(a,1,r) == 8); | |
1189 assert(r == 500); | |
1190 a = -8500; | |
1191 assert(truncate(a,1,r) == -8); | |
1192 assert(r == 500); | |
1193 a = -8500; | |
1194 // writeln("truncate(a, 6, r) = ", truncate(a, 6, r)); | |
1195 // writeln("r = ", r); | |
1196 assert(truncate(a,6,r) == -8500); | |
1197 assert(r == 0); | |
1198 writeln("passed"); | |
1199 } | |
1200 | |
1201 public Bcd pad(Bcd a, int n) { | |
1202 if (n <= a.numDigits) return a.dup; | |
1203 return Bcd(a,n); | |
1204 } | |
1205 | |
1206 unittest { | |
1207 write("pad........."); | |
1208 Bcd a; | |
1209 a = 1234567; | |
1210 assert(pad(a,3) == 1234567); | |
1211 a = 10256; | |
1212 // writeln("pad(a,9) = ", pad(a,9)); | |
1213 assert(pad(a,9).toString == "000010256"); | |
1214 a = 8500; | |
1215 assert(pad(a,6).toString == "008500"); | |
1216 a = -8500; | |
1217 assert(pad(a,6).toString == "-008500"); | |
1218 writeln("passed"); | |
1219 } | |
1220 public Bcd divide(const Bcd a, const Bcd b, out Bcd remainder) { | |
1221 Bcd quotient; | |
1222 Bcd dividend = stripLeadingZeros(a); | |
1223 // writeln("dividend = ", dividend); | |
1224 Bcd divisor = stripLeadingZeros(b); | |
1225 // writeln("divisor = ", divisor); | |
1226 if (divisor == ZERO) throw new Exception("Divide by zero"); | |
1227 remainder = dividend; | |
1228 writeln("remainder = ", remainder); | |
1229 Bcd next = divisor; | |
1230 // writeln("next = ", next); | |
1231 Bcd multiple; | |
1232 | |
1233 do { | |
1234 multiple = next; | |
1235 // writeln("multiple = ", multiple); | |
1236 next <<= 1; | |
1237 // writeln("next = ", next); | |
1238 } while (next <= remainder && next > multiple); | |
1239 | |
1240 while (multiple >= divisor) { | |
1241 quotient <<= 1; | |
1242 // writeln("quotient = ", quotient); | |
1243 while (multiple <= remainder) { | |
1244 remainder -= multiple; | |
1245 writeln("remainder = ", remainder); | |
1246 quotient++; | |
1247 // writeln("quotient = ", quotient); | |
1248 } | |
1249 multiple >>= 1; | |
1250 // writeln("multiple = ", multiple); | |
1251 } | |
1252 return quotient; | |
1253 } | |
1254 | |
1255 unittest { | |
1256 write("divide..."); | |
1257 writeln(); | |
1258 Bcd a, b, q, r; | |
1259 a = 144; | |
1260 b = 12; | |
1261 q = divide(a, b, r); | |
1262 assert(q == 12); | |
1263 assert(r == 0); | |
1264 a = 144; | |
1265 b = 14; | |
1266 q = divide(a, b, r); | |
1267 assert(q == 10); | |
1268 assert(r == 4); | |
1269 a = 142351; | |
1270 b = 12; | |
1271 q = divide(a, b, r); | |
1272 writeln("q = ", q); | |
1273 writeln("r = ", r); | |
1274 assert(q == 11862); | |
1275 assert(r == 7); | |
1276 a = 2; | |
1277 b = 12; | |
1278 q = divide(a, b, r); | |
1279 assert(q == 0); | |
1280 assert(r == 2); | |
1281 writeln("passed"); | |
1282 } | |
1283 | |
1284 | |
1083 public Bcd pow10(const Bcd a, uint n) { | 1285 public Bcd pow10(const Bcd a, uint n) { |
1084 Bcd b = a.dup; | 1286 Bcd b = stripLeadingZeros(a); |
1085 if (n == 0) return b; | 1287 if (n == 0 || b == 0) return b; |
1086 Digit[] zeros = new Digit[n]; | 1288 Digit[] zeros = new Digit[n]; |
1087 b.digits = zeros ~ b.digits; | 1289 b.digits = zeros ~ b.digits; |
1088 return b; | 1290 return b; |
1089 } | 1291 } |
1090 | 1292 |
1104 assert(a == Bcd("1000000000")); | 1306 assert(a == Bcd("1000000000")); |
1105 writeln("passed"); | 1307 writeln("passed"); |
1106 } | 1308 } |
1107 | 1309 |
1108 public Bcd shift(const Bcd a, int n) { | 1310 public Bcd shift(const Bcd a, int n) { |
1311 if (n == 0) return stripLeadingZeros(a); | |
1312 if (n > 0) { // shift left | |
1313 return pow10(a, n); | |
1314 } | |
1315 else { // shift right | |
1316 if (-n >= a.numDigits) return ZERO.dup; | |
1317 return truncate(a, a.numDigits + n); | |
1318 } | |
1319 } | |
1320 | |
1321 unittest { | |
1322 write("shift......."); | |
1323 Bcd a, b; | |
1324 a = 1234; | |
1325 b = shift(a, 1); | |
1326 assert(b.toString == "12340"); | |
1327 b = shift(a, 2); | |
1328 assert(b.toString == "123400"); | |
1329 b = shift(a, -1); | |
1330 // writeln("b = ", b); | |
1331 assert(b.toString == "123"); | |
1332 b = shift(a, 0); | |
1333 assert(b.toString == "1234"); | |
1334 b = shift(a, -12); | |
1335 /+ writeln("b = ", b);+/ | |
1336 assert(b.toString == "0"); | |
1337 b = shift(a, -4); | |
1338 assert(b.toString == "0"); | |
1339 writeln("passed"); | |
1340 } | |
1341 | |
1342 public Bcd shiftFixed(const Bcd a, int n) { | |
1109 if (n == 0) return a.dup; | 1343 if (n == 0) return a.dup; |
1110 Bcd b = Bcd(0, a.numDigits); | 1344 Bcd b = Bcd(0, a.numDigits); |
1111 if (std.math.abs(n) >= a.numDigits) return b; | 1345 if (std.math.abs(n) >= a.numDigits) return b; |
1112 if (n > 0) { // shift left | 1346 if (n > 0) { // shiftFixed left |
1113 b.digits[0..$-n] = a.digits[n..$]; | 1347 b.digits[0..$-n] = a.digits[n..$]; |
1114 } | 1348 } |
1115 else { // shift right | 1349 else { // shiftFixed right |
1116 n = -n; | 1350 n = -n; |
1117 b.digits[n..$] = a.digits[0..$-n]; | 1351 b.digits[n..$] = a.digits[0..$-n]; |
1118 } | 1352 } |
1119 return b; | 1353 return b; |
1120 } | 1354 } |
1121 | 1355 |
1122 unittest { | 1356 unittest { |
1123 write("shift..."); | 1357 write("shiftFixed..."); |
1124 Bcd a, b; | 1358 Bcd a, b; |
1125 a = 1234; | 1359 a = 1234; |
1126 b = shift(a, 1); | 1360 b = shiftFixed(a, 1); |
1127 assert(b.toString == "0123"); | 1361 assert(b.toString == "0123"); |
1128 b = shift(a, 2); | 1362 b = shiftFixed(a, 2); |
1129 assert(b.toString == "0012"); | 1363 assert(b.toString == "0012"); |
1130 b = shift(a, -1); | 1364 b = shiftFixed(a, -1); |
1131 assert(b.toString == "2340"); | 1365 assert(b.toString == "2340"); |
1132 b = shift(a, 0); | 1366 b = shiftFixed(a, 0); |
1133 assert(b.toString == "1234"); | 1367 assert(b.toString == "1234"); |
1134 b = shift(a, 12); | 1368 b = shiftFixed(a, 12); |
1135 assert(b.toString == "0000"); | 1369 assert(b.toString == "0000"); |
1136 b = shift(a, -4); | 1370 b = shiftFixed(a, -4); |
1137 assert(b.toString == "0000"); | 1371 assert(b.toString == "0000"); |
1138 writeln("passed"); | 1372 writeln("passed"); |
1139 } | 1373 } |
1140 | 1374 |
1141 public Bcd rotate(const Bcd a, int n) { | 1375 public Bcd rotate(const Bcd a, int n) { |