다음 코드에서는 질문과 같은 생각을 사용하여 더하기와 빼기를 구현합니다. 실제적인 차이점은 내 구현에서이 두 함수는 carry-in/borrow-in 비트를 가져 와서 carry-out/borrow-out 비트를 생성한다는 것입니다.
carry-in 비트는 덧셈을 통해 빼기를 구현하는 데 사용되며이 비트는 carry-out 및 borrow-out 비트의 올바른 값을 얻는 데 도움이됩니다. 기본적으로 상태 레지스터에 캐리 플래그가있는 전형적인 CPU와 같은 덧셈과 뺄셈을 구현합니다.
carry/borrow 비트는 뺄셈을 통해 비교를 구현하는 데 사용됩니다. 나는 비트가 현명한 성격이 아니기 때문에 산술을 고려한 >=
연산자를 사용하지 않고 비교를 구현합니다. 비교 함수는 restoring division algorithm을 사용하기 때문에 나누기 함수에서 필요합니다.
나는 !
연산자를 사용하지 않고 대신 ^1
을 사용합니다.
나누기 함수는 제수가 2 unsigned ints
이며, 가장 큰 부분과 가장 중요하지 않은 부분입니다. 마지막에는 가장 중요한 부분을 나머지와 가장 중요한 부분으로 바꿉니다. 따라서, 그것은 나눗셈과 모듈로를 모두 수행하고 일반적인 CPU와 비슷한 방식으로 수행합니다 (예 : x86 DIV
명령어와 같이). 이 함수는 성공시 1을 반환하고 오버 플로우/나눗셈이 0 인 경우 0을 반환합니다.
주 기능은 간단한 테스트를 수행합니다. 이 함수는 나누기 함수의 결과를 직접 나누기의 결과와 비교하고 불일치하는 오류 메시지와 함께 종료합니다.
테스트 부분에 unsigned long long
을 사용하면 무한 루프에 빠지지 않고 제수 = UINT_MAX
을 테스트 할 수 있습니다.배당금과 제수의 전체 범위를 테스트하는 데 너무 많은 시간이 걸릴 수 있습니다. 따라서 나는 UINT_MAX
대신 0xFFFF와 0xFF를 각각 캡핑합니다.
코드 : 당신은 ([이 간단한 방정식을 해결하기에 의해, 몫과 알림을 모두 계산 곱셈과 뺄셈을 결합해야처럼
#include <stdio.h>
#include <limits.h>
unsigned add(unsigned a, unsigned b, unsigned carryIn, unsigned* carryOut)
{
unsigned sum = a^b^carryIn;
unsigned carryOuts = a & b | (a | b) & carryIn;
*carryOut = 0;
if (sum & (carryOuts << 1))
sum = add(sum, carryOuts << 1, 0, carryOut);
else
sum |= carryOuts << 1;
*carryOut |= (carryOuts & (UINT_MAX/2 + 1)) >> (sizeof(unsigned) * CHAR_BIT - 1); // +-*/ are OK in constants
return sum;
}
unsigned sub(unsigned a, unsigned b, unsigned borrowIn, unsigned* borrowOut)
{
unsigned diff = add(a, ~b, borrowIn^1, borrowOut);
*borrowOut ^= 1;
return diff;
}
unsigned less(unsigned a, unsigned b)
{
unsigned borrowOut;
sub(a, b, 0, &borrowOut);
return borrowOut;
}
int udiv(unsigned* dividendh, unsigned* dividendl, unsigned divisor)
{
int i;
unsigned tmp;
if (less(*dividendh, divisor)^1/* *dividendh >= divisor */)
return 0; // overflow
for (i = 0; i < sizeof(unsigned) * CHAR_BIT; i++)
{
if (less(*dividendh, UINT_MAX/2 + 1)^1/* *dividendh >= 0x80...00 */)
{
*dividendh = (*dividendh << 1) | (*dividendl >> (sizeof(unsigned) * CHAR_BIT - 1));
*dividendl <<= 1;
*dividendh = sub(*dividendh, divisor, 0, &tmp);/* *dividendh -= divisor; */
*dividendl |= 1;
}
else
{
*dividendh = (*dividendh << 1) | (*dividendl >> (sizeof(unsigned) * CHAR_BIT - 1));
*dividendl <<= 1;
if (less(*dividendh, divisor)^1/* *dividendh >= divisor */)
{
*dividendh = sub(*dividendh, divisor, 0, &tmp);/* *dividendh -= divisor; */
*dividendl |= 1;
}
}
}
return 1;
}
int udiv2(unsigned* dividendh, unsigned* dividendl, unsigned divisor)
{
unsigned long long dividend =
((unsigned long long)*dividendh << (sizeof(unsigned) * CHAR_BIT)) | *dividendl;
if (*dividendh >= divisor)
return 0; // overflow
*dividendl = (unsigned)(dividend/divisor);
*dividendh = (unsigned)(dividend % divisor);
return 1;
}
int main(void)
{
unsigned long long dividend, divisor;
for (dividend = 0; dividend <= /*UINT_MAX*/0xFFFF; dividend++)
for (divisor = 0; divisor <= /*UINT_MAX*/0xFF; divisor++)
{
unsigned divh = 0, divl = (unsigned)dividend, divr = (unsigned)divisor;
unsigned divh2 = 0, divl2 = (unsigned)dividend;
printf("0x%08X/0x%08X=", divl, divr);
if (udiv(&divh, &divl, divr))
printf("0x%08X.0x%08X", divl, divh);
else
printf("ovf");
printf(" ");
if (udiv2(&divh2, &divl2, divr))
printf("0x%08X.0x%08X", divl2, divh2);
else
printf("ovf");
if ((divl != divl2) || (divh != divh2))
{
printf(" err");
return -1;
}
printf("\n");
}
return 0;
}
가 보이는 https://en.wikipedia.org/wiki/ 남아) – C2H5OH
Badd() - Mike Jones –
[숙제 지침] (http://meta.stackexchange.com/questions/147100/the-homework-tag-is-now-official-deprecated)에 따르면이 질문도 실용적인 프로그래밍 문제로 고안되어 실제 질문이 아닌 것으로 마감해야합니다. –