사칙연산을 구현할 때입니다.

사람들에게는 친숙하지만 컴퓨터에게는 친숙하지 않은가 봅니다.
제일 복잡합니다..



사칙연산 부분은 두단계를 거쳐 구현합니다.

private로 사칙연산의 알고리즘을 실행하는 함수를 만듭니다.
그리고 public으로 연산자 오버로딩을 사용하여 +,-,*,/,%,+=,-=,*=,/=,%=,++,--를 구현합니다.
private함수에서는 최대한 전제와 조건을 넣지 않으려고 하였습니다.
최대한 빠르게 하기 위해서 입니다.
private의 나누기 함수에서는 0으로 나누는지도 검사하지 않습니다.
연산을 하기 위한 모든 조건이 충족 됬다는 전제하에 private함수를 호출하는 것이기 때문입니다.
+, -, *, / 등등의 오버로딩 함수에서 모든 조건을 걸러내게 하였습니다.
(다른 멤버함수에서 호출하는 경우에 필요한 조건은 불가피하게 넣게 됬습니다.)


const BigInteger BigInteger::operator +(const BigInteger &x) const
{
	// 둘 중 하나가 0이면 다른 수를 리턴하면 됩니다.
	if (sign == Zero)
		return x;
	if (x.sign == Zero)
		return *this;

	BigInteger temp;
	CmpResult cmp;
	// CompareTo함수는 this와 x의 '크기'를 비교합니다.
	cmp = CompareTo(x);
	// 두 수의 부호가 다르다면, 뺄셈과 같습니다.
	if (sign != x.sign)
	{
		if (cmp == Equal)
			; // 연산 없이 0리턴
		else if (cmp == Greater)
		{
			// this > x인 경우
			temp.subtract(*this, x);
			temp.sign = sign;
		}
		else
		{
			// x > this인 경우
			temp.subtract(x, *this);
			temp.sign = x.sign;
		}
	}
	// 부호가 같은경우..
	else
	{
		temp.add(*this, x);
		temp.sign = sign;
	}
	return temp;
}


+ 연산자 오버로딩 부분입니다.

두 수중 0이 있는지..
두 수의 부호가 같은지 다른지..
두 수의 길이가 같은지 다른지..
에 따라 호출되는 함수가 다르며
넘어가는 인수의 순서도 다릅니다.

위 함수에서는 add 혹은 subtract 함수가 호출됩니다.
this->add(a, b)함수는 a,와 b의 크기의 함을 this에 저장합니다.
(a, b의 부호는 무시하고 크기만을 더합니다. )
this->subtract(a, b)함수는 a의 크기에서 b의 크기를 뺍니다.
(이것도 역시 부호는 무시합니다.)

여기서는 add함수에 대해서만 포스팅 하겠습니다.
subtract 함수는 다음글에서 쓰겠습니다. 


void BigInteger::add(const BigInteger &Left, const BigInteger &Right)
{
	// Left와 Right의 크기를 더하여 this에 저장하는 함수입니다.

	// StringToBiginteger 함수에서
	// this->add(*this, x)와 같이 호출하기 때문에
	if (this == &Left)
	{
		BigInteger Left_temp(Left);
		add(Left_temp, Right);
		return;
	}
	// 호출 함수부분을 간략하게 하기 위해서
	if (Left.len < Right.len)
	{
		add(Right, Left);
		return;
	}
	// 이제, Left와 Right의 길이간에는
	// Left.len >= Right.len 이 성립
	Reallocate(Left.len + 1);

	blocktype blk_temp = 0; // 두 블럭의 합을 잠시 저장하기 위해
	// 자리수 올림 처리용
	// carryIn : 이전 자리에서 자리올림 발생?
	// carryOut : 현 자리에서 연산으로 자리올림 발생?
	bool carryIn, carryOut;
	index i;
	// 낮은 자리 만큼 루프를 돌며 덧셈 연산
	for(i = 0, carryIn = false; i < Right.len; i++){
		blk_temp = Left.blk[i] + Right.blk[i];
		// 자리 올림이 발생하면
		// blk_temp < Left.blk[i]
		// blk_temp < Right.blk[i]
		// 위 두개를 모두 만족한다.
		// 그래서 두 개중에 하나만 사용하여 비교해 주면된다.
		carryOut = (blk_temp < Left.blk[i]);
		if (carryIn) { // 전 자리에서 자리올림 발생했는가?
			++blk_temp;
			// CarryIn으로 인한 자리올림 발생 가능성 처리
			carryOut |= (blk_temp==0);
		}
		blk[i] = blk_temp;
		carryIn = carryOut;
	}

	// blk[Right.len - 1]에서 자리올림 발생했는가?
	// 또한, 큰 가능성은 없지만 연쇄 자리올림이 가능하다
	// ex) 799 + 1과 같은 경우와 비슷한 맥락
	for(; carryIn && i < Left.len; i++){
		blk[i] = Left.blk[i] + 1;
		carryIn = (blk[i] == 0);
	}

	// 나머지는 그대로 대입
	for(; i < Left.len; i++){
		blk[i] = Left.blk[i];
	}

	// 연쇄 자리올림이 끝까지 일어난 경우이다.
	// 위에서 799 + 1이 아닌 999 + 1과 같은 경우다.
	// 연산값이 자릿수가 1 증가하며 증가한 자리의 숫자는 1이다.
	// 아니라면 len 1 감소
	if(carryIn) blk[i]=1;
	else len--;
}


정성스럽게 주석을 달았습니다.. ^^
주석만 잘보시면 이해되실겁니다. ^^ 
Posted by 투명테잎