int32整型除法的位运算模拟
问题说明:
int32整型除法:
想要求解的问题如下:
数值溢出问题:
- 对于整形int来说,它的值域为 。每次进行运算时,可能会产生溢出。特别地,int型最小值,在取反的时候,也会溢出。
- 对于无符号型数,也要警惕其产生溢出(比如unsigned加法产生溢出,会导致两个unsigned之和小于二者任意一个)。
解法:
搜索的思想 二分法求解:
思路:
求解|A|
|B|的整体思路就是找到一个数C,使得C是满足Cx|B|
|A|的最大的非负整数。
因此,完全可以使用二分搜索法,在区间[0,A]中搜寻C的存在。时间复杂度是O(log(|A|))级别。
代码:
class Solution {
public:
unsigned const Max_of_Unsigned=0xffffffffu;
int const Min_Nega_Int=0x80000000;
int const MaX_Posi_Int=0x7fffffff;
public:
int divide(int dividend, int divisor)
{
bool isNegative=false;
if((dividend<0&&divisor>0)||(dividend>0&&divisor<0))
isNegative=true;
unsigned res=true_divide(to_Abs_Unsigned(dividend),to_Abs_Unsigned(divisor));
if(isNegative)
{
return to_Negative_Signed(res);
}
else
{
if(res==0x80000000u)
return 0x7fffffff;
return (int)res;
}
}
/**
* 将一个int型变量x,转换为值为该int型绝对值|x|,的unsigned型变量的方法
*/
public:unsigned to_Abs_Unsigned(int x)
{
if(x>=0)
return (unsigned)x;
if(x==0x80000000)
return 0x80000000u;
return (unsigned)(~x+1);
}
/**
* 将一个unsigned型的变量转换为,值为其相反数-x,的型为int的变量
*/
public:int to_Negative_Signed(unsigned x)
{
if(x==0x80000000u)
return 0x80000000;
x=~x+1;
return *(int*)(&(x));
}
/**
* 将除数与被除数用unsigned表示,保证不超出范围,然后调用该函数使用二分法解题
* 参数:div和dis都是不大于0x80000000的数
*/
public:unsigned true_divide(unsigned div,unsigned dis)
{
cout<<"\t"<<div<<"\t"<<dis<<endl;
unsigned max=div;
unsigned min=0u;
if(max<dis)
return 0u;
if(dis==1u)
return div;
unsigned mid;
while(max>min+1)
{
//0x80000000>=max>min+1 因此加法不会溢出
mid=(max+min)>>1;
unsigned check=multiply(std::max(mid,dis),std::min(mid,dis));
if(check==div)
return mid;
else if(check<div)
{
min=mid;
}
else
{
max=mid-1;
}
}
cout<<"\tmax: "<<max<<"\tmin: "<<min<<endl;
if(max==min)
return max;
else
{
if(multiply(std::max(max,dis),std::min(max,dis))<=div)
return max;
else
{
return min;
}
}
}
//返回两个unsigned代表的数之积,如果超过unsigned表示范围,则返回0xffffffffu
public:unsigned multiply(unsigned x,unsigned y)
{
unsigned acc=x;
unsigned res=0u;
unsigned judge=y;
unsigned unchecked_sum=0u;
while(judge>0u)
{
if(judge&0x1u!=0u)
{
unchecked_sum=res+acc;
if(unchecked_sum<res||unchecked_sum<acc)
return Max_of_Unsigned;
res=unchecked_sum;
}
judge=judge>>1;
if(acc>=0x80000000u&&judge>0u)
return Max_of_Unsigned;
acc=acc<<1;
}
return res;
}
};
二进制思想 位运算求解:
思路:
- 将32位整数C写作其二进制表示 。可以通过从高位到低位直接遍历一遍 的每一位,得到C的二进制表达形式,从而C可求。
- 从第31位开始,对于C的每一位 是否为1,仅仅取决于 是否大于等于 。
原理:
- 在遍历 的数位时,第 位可以表示最大的数是 ,而后i-1位可以表示的最大的数是 。从而如果已经知道C的后 位值 要大于等于 ,那么一定有 。
- 因为在遍历到第i位时,
前的每一位已经确定,则可以通过
来确定C的后
位值
。
代码:
class Solution1 {
public:
int divide(int dividend, int divisor) {
if (dividend == INT_MIN && divisor == -1) {
return INT_MAX;
}
bool isNegative = (dividend > 0) ^ (divisor > 0);
unsigned int a = (dividend == INT_MIN) ? 0x80000000 : abs(dividend);
unsigned int b = (divisor == INT_MIN) ? 0x80000000 : abs(divisor);
unsigned int result = 0;
for (int i = 31; i >= 0; i--) {
if ((a >> i) >= b) {
result += ((unsigned int)1) << i;
a -= b << i;
}
}
if (result == 0x80000000) {
return INT_MIN;
}
else {
return (isNegative) ? -(int)result : (int)result;
}
}
};