BCDの加算と減算

ちょっと必要があって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);
	} 
}