求商和余数

等待升级 2011-08-19 10:25:07
计算int a 除以 2的b次方 b为unsigned

例如(-285/8)

要求不使用除法,要求高效率 (即使用移位操作 )
...全文
1146 27 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
27 条回复
切换为时间正序
请发表友善的回复…
发表回复
zrd1989 2012-03-14
  • 打赏
  • 举报
回复
带负数的商和余是分情况的 具体的想不起来了 c++ primer中文第4版 具体书名记不得了 上面有 现在没带书 无法具体 就是第四版本的那个 很畅销的那本 红皮的
CJacky++ 2011-08-28
  • 打赏
  • 举报
回复
哇赛,我比较关心这个式子是怎么推出来的。
x / (2^k) = (x < 0 ? (x + (1 << k) - 1) : x) >> k
等待升级 2011-08-27
  • 打赏
  • 举报
回复
好,写的都非常好
mymtom 2011-08-21
  • 打赏
  • 举报
回复
[Quote=引用 21 楼 frais 的回复:]

看了下c陷阱与缺陷的7.7节
再根据 -5/2 -5%2 在vc中的结果(-2,-1)

说明 -5>>1 = -3的实现是不太合适的,即负数的除法通过简单的移位实现是不正确的,应该转换为正数移位
[/Quote]
这个不难解决,只需判断被除数的符号,并做相应调整即可

if (num < 0 && *rem > 0) {
(*quot)++;
*rem -= denom;
}



/**
* @file div2.c
* @brief
*/

#include <stdio.h>

int div2(int num, int bit, int *quot, int *rem)
{
unsigned int mask;
unsigned int denom;

denom = 1 << bit;
mask = denom - 1;
*quot = num >> bit;
*rem = num & mask;
if (num < 0 && *rem > 0) {
(*quot)++;
*rem -= denom;
}
return *quot;
}

int main(void)
{
int num, bit, quot, rem;

num = 5, bit = 1;
div2(num, bit, ", &rem);
printf("%d/%d = %d+%d/%d\n", num, 1 << bit, quot, rem, 1 << bit);

num = -5, bit = 1;
div2(num, bit, ", &rem);
printf("%d/%d = %d+%d/%d\n", num, 1 << bit, quot, rem, 1 << bit);

return 0;
}
mymtom 2011-08-21
  • 打赏
  • 举报
回复

/**
* @file div2.c
* @brief
*/

#include <stdio.h>

int div2(int num, int bit, int *quot, int *rem)
{
int mask;

mask = (1 << bit) - 1;
*quot = num >> bit;
*rem = num & mask;
return *quot;
}

int main(void)
{
int num, bit, quot, rem;

num = 15, bit = 2;
div2(num, bit, ", &rem);
printf("%d/%d = %d+%d/%d\n", num, 1 << bit, quot, rem, 1 << bit);

num = -15, bit = 2;
div2(num, bit, ", &rem);
printf("%d/%d = %d+%d/%d\n", num, 1 << bit, quot, rem, 1 << bit);

return 0;
}
icemornings 2011-08-21
  • 打赏
  • 举报
回复
翻了一下书,重新写了个:

#include <stdio.h>

/* 定义个类型,主要是为了方便返回商和余数 */
struct DIV_T
{
/* 商 */
int quot;
/* 余数 */
int rem;
};

/*
有符号数除法,负数的时候要加上一个偏置量即
x / (2^k) = (x < 0 ? (x + (1 << k) - 1) : x) >> k
这样的话就会向0取整,比如-5 / 2 = -2.5 = -2
*/
struct DIV_T xDiv(int a, int b)
{
/* 符号位,这是一个常数,在编译时候就能计算出来 */
enum {SIGN_BIT = 1 << ((sizeof(int) << 3) - 1)};
/* 是否为负数 */
int neg = a & SIGN_BIT;
/* 偏置量,负数的话要加上偏置量 */
int bias = ((1 - !neg) << b) - (1 && neg);

struct DIV_T tmp;

tmp.quot = (a + bias) >> b;
tmp.rem = a - (tmp.quot << b);

return tmp;

}

int main(int argc, char **argv)
{
int a = -285, b = 3;
struct DIV_T result = xDiv(a, b);

printf("a = %d, b = %d, 2^b = %d\nquot = %d, rem = %d\n", a, b, (1 << b), result.quot, result.rem);
return 0;
}
ljhhh0123 2011-08-20
  • 打赏
  • 举报
回复
那不简单?全都转成unsigned计算,最后根据被除数与除数的正负来决定结果的正负。
此类问题请参看《C语言接口与实现》第2章。
jernymy 2011-08-20
  • 打赏
  • 举报
回复
写的有点复杂,待优化

#include <stdio.h>

#define MOVE_BIT (sizeof(int) * 8 - 1) // 32bit is 31, 16bit is 15

#define TIVE_V(a) (a) = (~(a) + 1)
// Remainder
// Divisor

void GetDivRmd(int nVar, int nBit, int *pnDiv, int *pnRmd)
{
int nPos = ((nVar >> MOVE_BIT) & 0x1); // Positive number or Negative number
int nCount = 0;

while (nBit > 1)
{
// get 2 bit number
nCount++;
nBit >>= 1;
}

if (nPos) // Negative number
{
TIVE_V(nVar); // get abs var, is Positive number
}

*pnDiv = nVar >> (nCount);
*pnRmd = nVar - (*pnDiv << nCount);

if (nPos)
{
TIVE_V(*pnDiv); // set Negative number
TIVE_V(*pnRmd); // set Negative number
}
}


int main(void)
{
int nDiv;
int nRmd;

GetDivRmd(285, 8, &nDiv, &nRmd);
printf("285/8 = %d(%d)\n", nDiv, nRmd);
GetDivRmd(-285, 8, &nDiv, &nRmd);
printf("-285/8 = %d(%d)\n", nDiv, nRmd);
return 0;
}

Jade0709 2011-08-20
  • 打赏
  • 举报
回复
这个貌似以前见到过这个问题,定义一下三条规则应该可以解决:
1.被除数=除数*商+余数
2.余数与除数同号
3.余数绝对值<除数绝对值

如此:5/(-3) = -2 ....-1
(-5)/3 = -2......1
等待升级 2011-08-20
  • 打赏
  • 举报
回复
[Quote=引用 10 楼 ljljlj 的回复:]
既然楼主说巨简单,斗胆写下下面的代码。记得你说得是unsigned的啊。

C/C++ code
#include<stdio.h>
int main(){
unsigned a,b;
while(scanf("%u %u", &a,&b)){//b是输出的2的指数,输入3意思是8
printf("quot==%u\n", a>>b);……
[/Quote]
我只说过b是unsigned,a明显不是
顺便帮忙解释下6楼的疑问
ljhhh0123 2011-08-20
  • 打赏
  • 举报
回复
既然楼主说巨简单,斗胆写下下面的代码。记得你说得是unsigned的啊。
#include<stdio.h>
int main(){
unsigned a,b;
while(scanf("%u %u", &a,&b)){//b是输出的2的指数,输入3意思是8
printf("quot==%u\n", a>>b);
printf("rem==%u\n", a&b);
}
}
等待升级 2011-08-20
  • 打赏
  • 举报
回复
up ppp
等待升级 2011-08-20
  • 打赏
  • 举报
回复
看了下c陷阱与缺陷的7.7节
再根据 -5/2 -5%2 在vc中的结果(-2,-1)

说明 -5>>1 = -3的实现是不太合适的,即负数的除法通过简单的移位实现是不正确的,应该转换为正数移位
ljhhh0123 2011-08-20
  • 打赏
  • 举报
回复
纠正10楼的错误,sorry。
1.内嵌的操作只对正整数有效,其它的则未定义。
#include<stdio.h>
int main(){
unsigned a,b;
while(scanf("%u %u", &a,&b)){//b是输出的2的指数,输入3意思是8
printf("%u/%u quot==%u\n",a, 1<<b, a>>b);
printf("rem==%u\n", a&((1<<b)-1));
}
}

2.标准库函数div定义了向0取值。
#include<stdio.h>
#include<stdlib.h>
int main(){
int a,b;
while(scanf("%d %d", &a,&b)){//b是指数,输入3意思是8
printf("%d/%d quot==%d\n",a, 1<<b, div(a,1<<b).quot);
printf("rem==%d\n", div(a,1<<b).rem);
}
}

3.:《C语言接口与实现》第2章实现的Arith接口用更精确的数学术语定义,商向数轴左侧取值。
Jade0709 2011-08-20
  • 打赏
  • 举报
回复
这个问题C陷阱与缺陷的7.7节讲到了。
Jade0709 2011-08-20
  • 打赏
  • 举报
回复
[Quote=引用 17 楼 frais 的回复:]

大家说的都很有道理

我想解决的其实是负数时候的问题

比如 -5/2 呢? 商-2 余-1 呢还是 商-3 余1

Jade0709 同志提出

这个貌似以前见到过这个问题,定义一下三条规则应该可以解决:
1.被除数=除数*商+余数
2.余数与除数同号
3.余数绝对值<除数绝对值

-5/2 应该是 商-3 余 1 了 ,-5 >> 1 = -3 倒是不错
……
[/Quote]
这三条是没有问题的,对于编译器来说确定了这三条规则便可以唯一确定除法的商和余数,当然并不是所有的编译器都是这样的,还可以:
1.被除数=除数*商+余数
2.余数与被除数同号
3.余数绝对值<除数绝对值
那么:5/(-3)= -1.....2
(-5)/3=-1....-2
总之,重要的是编译器必须保证商和余数的唯一性,C语言没有做强制性要求。
另外,你觉得实际数学中应该是什么样子呢?
等待升级 2011-08-20
  • 打赏
  • 举报
回复
大家说的都很有道理

我想解决的其实是负数时候的问题

比如 -5/2 呢? 商-2 余-1 呢还是 商-3 余1

Jade0709 同志提出

这个貌似以前见到过这个问题,定义一下三条规则应该可以解决:
1.被除数=除数*商+余数
2.余数与除数同号
3.余数绝对值<除数绝对值

-5/2 应该是 商-3 余 1 了 ,-5 >> 1 = -3 倒是不错
好像不是太符合实际数学中的样子吧
望有解决经验的同志给予解释
CJacky++ 2011-08-20
  • 打赏
  • 举报
回复
a 为正时.
商 = a >> b;
余数 = a & ((1<<b) - 1);
a 为负时,不同的编译器给出的结果可能不一样。无从下手,请高手指教。
Jxiaoshen 2011-08-20
  • 打赏
  • 举报
回复
[Quote=引用 6 楼 frais 的回复:]
5/2 商 2 余 1
-5/2 呢? 商-2 余-1 呢还是 商-3 余1
[/Quote]
根据编译器
yanpengit 2011-08-19
  • 打赏
  • 举报
回复
-5 = -2x2 + (-1).......商2余-1
加载更多回复(7)

70,020

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧