Выполнение битового деления без арифметических операторов
Я пытаюсь выполнить задание, которое требует от меня написать три функции для двоичной арифметики. Для меня был предоставлен badd (), поэтому я использовал его для написания функций bsub () и bmult (). Однако у меня возникают проблемы с пониманием того, как я должен выполнять функцию bdiv (). Я знаю, что мне нужно перебирать биты, используя сдвиг вправо и мою функцию bsubb (), но я не знаю, как это реализовать. Ниже приведены функции, которые я написал до сих пор. Дайте мне знать, если вы заметили какие-либо ошибки, которые я допустил при их написании (имеется в виду bsub () и bmult ()). Благодарю.
/** This function adds the two arguments using bitwise operators. Your
* implementation should not use arithmetic operators except for loop
* control. Integers are 32 bits long. This function prints a message
* saying "Overflow occurred\n" if a two's complement overflow occurs
* during the addition process. The sum is returned as the value of
* the function.
*/
int badd(int x,int y){
int i;
char sum;
char car_in=0;
char car_out;
char a,b;
unsigned int mask=0x00000001;
int result=0;
for(i=0;i<32;i++){
a=(x&mask)!=0;
b=(y&mask)!=0;
car_out=car_in & (a|b) |a&b;
sum=a^b^car_in;
if(sum) {
result|=mask;
}
if(i!=31) {
car_in=car_out;
} else {
if(car_in!=car_out) {
printf("Overflow occurred\n");
}
}
mask<<=1;
}
return result;
}
// subracts two integers by finding the compliemnt
// of "y", adding 1, and using the badd() function
// to add "-y" and "x"
int bsub(int x, int y){
return badd(x, badd(~y, 1));
}
//add x to total for however many y
int bmult(int x,int y){
int total;
int i;
for(i=0; i < = y; i++)
{
total = badd(total,x)
}
return total;
}
// comment me
unsigned int bdiv(unsigned int dividend, unsigned int divisor){
// write me
return 0;
}
3 ответа
Здесь не так много, просто базовая математика в base-2:
unsigned int bmult(unsigned int x, unsigned int y)
{
int total = 0;
int i;
/* if the i-th bit is non-zero, add 'x' to total */
/* Multiple total by 2 each step */
for(i = 32 ; i >= 0 ; i--)
{
total <<= 1;
if( (y & (1 << i)) >> i )
{
total = badd(total, x);
}
}
return total;
}
unsigned int bdiv(unsigned int dividend, unsigned int divisor)
{
int i, quotient = 0, remainder = 0;
if(divisor == 0) { printf("div by zero\n"); return 0; }
for(i = 31 ; i >= 0 ; i--)
{
quotient <<= 1;
remainder <<= 1;
remainder |= (dividend & (1 << i)) >> i;
if(remainder >= divisor)
{
remainder = bsub(remainder, divisor);
quotient |= 1;
}
}
return quotient;
}
Этих двух статей достаточно, чтобы закодировать эти образцы: Div и Mul.
В следующем коде я реализую сложение и вычитание, используя ту же идею, что и в вопросе. Единственное практическое отличие состоит в том, что в моей реализации эти две функции также принимают бит переноса / заимствования и формируют бит выноса / заимствования.
Бит переноса используется для реализации вычитания посредством сложения, и этот бит помогает получить правильные значения битов выноса и заимствования. По сути, я реализую типичное CPU-подобное сложение и вычитание с флагом переноса в регистре состояния.
Затем биты переноса / заимствования используются для осуществления сравнения посредством вычитания. Я реализую сравнение без >=
оператор, который я тоже считаю арифметическим, из-за его не совсем побитовой природы. Функция сравнения необходима в функции деления из-за использования восстанавливающего алгоритма деления.
Я также избегаю использования !
оператор и использование ^1
вместо.
Функция деления принимает делитель как 2 unsigned ints
наиболее и наименее значимые его части. В конце он заменяет наиболее значимую часть на остаток, а наименее значимую часть - частным. Таким образом, он выполняет и деление, и по модулю, и выполняет их типично для процессора (например, для x86). DIV
инструкция). Функция возвращает 1 в случае успеха и 0 при переполнении / делении на 0.
Основная функция делает простой тест. Он сравнивает результаты функции деления с результатами прямого деления и завершается сообщением об ошибке несоответствия.
я использую unsigned long long
в тестовой части, чтобы иметь возможность проверить делитель =UINT_MAX
не попадая в бесконечный цикл. Может потребоваться слишком много времени для проверки всего диапазона значений дивиденда и делителя, поэтому я ограничиваю их значениями 0xFFFF и 0xFF соответственно вместо UINT_MAX
,
Код:
#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;
}
- в то время как дивиденд<делитель добавляет ноль после десятичной точки в частном и сдвигает правый дивиденд на 1
- Теперь проверьте, равно ли число битов в делителе количеству битов в делимом, если нет, делите сдвиг влево до тех пор, пока число битов в делителе не станет равным количеству бит в делителе.
- Теперь вычтите дивиденд, разделите и добавьте 1 к частному, убедитесь, что у вас есть 1 к частному в правильном месте (как десятичная позиция)
Повторяйте процесс, пока дивиденд не станет равным 0 или 1