ちょっと必要があってBCD(二進化十進数、Binary-coded decimal)同士の加算と減算をするコードを書いてみた。条件を整理すればもう少し簡潔にできそうだけど、くたびれてきたので今日はここまで。
//---- Header part ------------------------------------------------------ int bcdcmp(const char *num1, const char *num2); int bcdsub(const char *num1, const char *num2, char *ans, int maxlen); int bcdadd(const char *num1, const char *num2, char *ans, int maxlen); void bin2bcd(int bin, char* bcd); void bcd2bin(const char* bcd, int *bin); //----- Program part --------------------------------------------------- #include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h> /******************************************************************* * * Add and Subtract signed BCD numbers * ******************************************************************/ /*! @brief reverse an array of character @param array array of character @param len length of array @note file scope function */ static void reverse_char_arry(char *array, int len) { int i; int last = len - 1; char tmp; for (i = 0; i <= last / 2; ++i) { tmp = array[i]; array[i] = array[last - i]; array[last - i] = tmp; } } /*! @brief compare bcd number @retval +1 when num1 > num2 @retval 0 when num1 == num2 @retval -1 when num1 < num2 */ int bcdcmp(const char *num1, const char *num2) { int sign; int len1, len2; if (num1[0] == '-' && num2[0] == '-') { return bcdcmp(&num2[1], &num1[1]); } else if (num1[0] == '-' && num2[0] != '-') { return -1; } else if (num1[0] != '-' && num2[0] == '-') { return +1; } len1 = strlen(num1); len2 = strlen(num2); if (len1 < len2) { return -1; } else if (len1 > len2) { return +1; } else { sign = strcmp(num1, num2); if (sign == 0) { return 0; } return sign / abs(sign); } } /*! @brief ans = num1 - num2 @param num1 BCD number @param num2 BCD number @param ans Answer buffer @param maxlen size of anwer buffer @retval 0 fail @retval 1 success */ int bcdsub(const char *num1, const char *num2, char *ans, int maxlen) { const char *num_1, *num_2; int sign; int pos1, pos2, pos_ans; int n1, n2; int diff, borrow; if (num1[0] == '-' && num2[0] == '-') { return bcdsub(&num2[1], &num1[1], ans, maxlen); } else if (num1[0] == '-' && num2[0] != '-') { ans[0] = '-'; return bcdadd(&num1[1], num2, &ans[1], maxlen-1); } else if (num1[0] != '-' && num2[0] == '-') { return bcdadd(num1, &num2[1], ans, maxlen); } sign = bcdcmp(num1, num2); if (sign < 0) { num_1 = num2; //num1 - num2 -> -1 * (num2 - num1) num_2 = num1; } else { num_1 = num1; num_2 = num2; } pos1 = strlen(num_1) - 1; pos2 = strlen(num_2) - 1; pos_ans = 0; ans[pos_ans++] = '\0'; borrow = 0; while (pos1 >= 0 || pos2 >= 0) { if (pos1 < 0) { n1 = 0; } else { n1 = num_1[pos1--] - '0'; } if (pos2 < 0) { n2 = 0; } else { n2 = num_2[pos2--] - '0'; } diff = n1 - n2 - borrow; if (diff < 0) { diff += 10; borrow = 1; } else { borrow = 0; } ans[pos_ans++] = (char)(diff + '0'); if (pos_ans >= maxlen) { return 0; } } if (ans[pos_ans - 1] == '0') { pos_ans--; } if (sign < 0) { ans[pos_ans++] = '-'; } reverse_char_arry(ans, pos_ans); return 1; } /*! @brief ans = num1 + num2 @param num1 BCD number @param num2 BCD number @param ans Answer buffer @param maxlen size of anwer buffer @retval 0 fail @retval 1 success */ int bcdadd(const char *num1, const char *num2, char*ans, int maxlen) { int pos1, pos2, pos_ans; int n1, n2; int sum, carry; if (num1[0] == '-' && num2[0] == '-') { ans[0] = '-'; return bcdadd(&num1[1], &num2[1], &ans[1], maxlen-1); } else if (num1[0] == '-' && num2[0] != '-') { return bcdsub(num2, &num1[1], ans, maxlen); } else if (num1[0] != '-' && num2[0] == '-') { return bcdsub(num1, &num2[1], ans, maxlen); } pos1 = strlen(num1) - 1; pos2 = strlen(num2) - 1; pos_ans = 0; ans[pos_ans++] = '\0'; carry = 0; while (pos1 >= 0 || pos2 >= 0) { if (pos1 < 0) { n1 = 0; } else { n1 = num1[pos1--] - '0'; } if (pos2 < 0) { n2 = 0; } else { n2 = num2[pos2--] - '0'; } sum = n1 + n2 + carry; if (sum >= 10) { sum -= 10; carry = 1; } else { carry = 0; } ans[pos_ans++] = (char)(sum + '0'); if (pos_ans >= maxlen) { return 0; } } if (carry != 0) { ans[pos_ans++] = '1'; } reverse_char_arry(ans, pos_ans); return 1; } /*! @brief BIN to BCD @param bin BIN number @param bcd Pointer to BCD number @param maxlen size of anwer buffer */ void bin2bcd(int bin, char* bcd) { sprintf(bcd, "%d", bin); } /*! @brief BCD to BIN @param bcd Pointer to BCD number @param bin Pointer to BIN number */ void bcd2bin(const char* bcd, int *bin) { sscanf(bcd, "%d", bin); } //----- Test part ------------------------------------------------------ #define LEN 20 #define TRIAL 10000 void main(void) { int err_cnt = 0; int i, ret; char buff[LEN]; int bin1, bin2; char bcd1[LEN], bcd2[LEN], ans[LEN]; srand((unsigned) time(NULL)); for (i = 0; i < TRIAL; ++i) { bin1 = rand(); if ((bin1/10)%2 == 0) { bin1 *= -1; } bin2bcd(bin1, bcd1); bin2 = rand(); if ((bin2/10)%2 == 0) { bin2 *= -1; } bin2bcd(bin2, bcd2); //Test bcd2bin and bin2bcd bcd2bin(bcd1, &ret); if (bin1 != ret) { printf("%d = %s (!=%d)\n", bin1, bcd1, ret); ++err_cnt; } //Test bcdcmp ret = bcdcmp(bcd1, bcd2); if (bin1 < bin2) { if (! (ret < 0)) { printf("(%d) < (%d),\t\t (%s) !< (%s)\n", bin1, bin2, bcd1, ret, bcd2); ++err_cnt; } } else if (bin1 > bin2) { if (! (ret > 0)) { printf("(%d) > (%d),\t\t (%s) !> (%s)\n", bin1, bin2, bcd1, ret, bcd2); ++err_cnt; } } else { if (ret != 0) { printf("(%d) == (%d),\t\t (%s) != (%s)\n", bin1, bin2, bcd1, ret, bcd2); ++err_cnt; } } //Test bcdadd bcdadd(bcd1, bcd2, ans, LEN); bcd2bin(ans, &ret); if (bin1 + bin2 != ret) { printf("(%s) + (%s) = %s (!=%d)\n", bcd1, bcd2, ans, bin1 + bin2); ++err_cnt; } //Test bcdsub bcdsub(bcd1, bcd2, ans, LEN); bcd2bin(ans, &ret); if (bin1 - bin2 != ret) { printf("(%s) - (%s) = %s (!=%d)\n\n", bcd1, bcd2, ans, bin1 - bin2); ++err_cnt; } } if (err_cnt == 0) { printf("No Error. (%d trial)\n", TRIAL); } }