第一次面试的时候,被问到这么一道题:
计算机中数据是以二进制来保存的,如何先将一个数的二进制形式逆序,然后再把新得到的数用整数形式输出?
比如13表示为 1101, 逆序后位 1011,用整数形式输出结果为11。
当时自己想的很复杂, 主要考虑了两种情况,一种是不考虑原始数据前面隐藏的0,从第一位不为0的数开始算起,进行逆序;第二种情况是考虑全部32位,前面隐藏的0在逆序后,全部位于新数的后面。当时自己也没问清楚,就随便写了一种,现在想想,还有可能需要考虑符号位的问题,不过涉及到符号位,更需要考虑计算机中负数的保存方式,不能单单以直观方式来考虑了。
先写出前面两种的解决方案:
第一种,不考虑原始数据前面隐藏的0,这种情况较为一般,方法也有很多种,不过最多的就是将每位保存至数组中,然后逆序在变成十进制数,
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void main() {
int v;
char b[33],*ep;
scanf("%d",&v);//输入13回车
itoa(v,b,2); //转化为2进制数组
strrev(b); //将数组逆序
v=strtol(b,&ep,2); //转化为十进制长整形,ep保存异常输出。
printf("%d\n",v);//输出11
}
这种方法使用了库函数,最为简便,如果没有特殊要求不能使用库函数,则该方法最为便捷。
void main()
{
int a = 13;
int s = 0;
while(a)
{
s<<=1;
s |= (0x01 & a);
a>>=1;
}
cout<<s<<endl;
system("pause");
return;
}
这种方法通过灵活的移位操作,来达到目的。先让s左移,来获取为最后一位腾出空间,再通过或运算来得到当前a最后一位的值,直到a全部移位完成。需要注意的是,如果
s |= (0x01 & a);
s<<=1;
这两句执行顺序这样的话,虽然也能保证保存取到a当前的最后一位,不过会导致s比期望的结果多向左移动一位。
#include<stdio.h>
int main()
{
char str[31];//偷个懒,不考虑符号位了...
int num,i=0,j,k,tmp;
printf("Please input a num:");
scanf("%d",&num);
do
{
str[i]=num%2+'0';
num/=2;
i++;
}
while(num>1);
str[i]=num+'0';
num=0;
for(j=0;j<=i;j++)
{
k=i-j;
tmp=1;
while(k--)tmp*=2;
num+=(str[j]-'0')*tmp;
}
printf("%d\n",num);
return 0;
}
这个方法是最为普通的方法,通过保存每一位,来达到目的。
值得一提的是,有一种将数值快速逆序的方法,是一个网友提供的方法,如下示例中,将1进行了逆序:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
int n = 0x1, rev = 0;
char bits[100];
for (int i = 0; i < sizeof(int) << 3; ++ i) //32位移动 sizeof(int) << 3 取值方法很巧妙
{
if ((1 << i) & n)
{
rev = (rev << 1) + 1;
}
else
{
rev = (rev << 1) + 0;
}
}
printf("%x\n", rev);
return 0;
}
这种方法通过判断原始数据每一位的值来获取每一次移位得到的数据。注意的是,如果为当前位为1,加1补齐结果。如果为0,不需要补齐。因为判断n的当前位始终为结果数值的最后移位,通过加0加1来与判断的当前位保持一致。
在第二种情况中,下面的方法很好的提供了解决方案:
int = 31, i32OutPut = 0;
int i32Value;
scanf("%d", & i32Value);
while (i--) {
i32OutPut |= (((i32Value >> i) & 1) ? (1 << (31 - i)) : 0);
}
printf("%d\n", i32OutPut);
这种方法也很简单,通过对1移位或是补0来保证结果的一致性,需要注意的是,因为数据是以32位保存的,移动的时候只需移动31位就可以,如果移动32位,会导致溢出。
在第三种情况中,情况也应该是大致类似,不过首先应考虑补码的保存,确保符号位。我对这个也不是很了解,还要多进行学习。