contents:
1)任意进制转化为10进制
2)10进制转换为任意进制
3)大数任意进制转化
一.任意进制转化为10进制:
原理:
这个最好理解。首先要知道:不同数位的权重不同
以一个十进制数186为例:
个位上的6代表的值是6×,即6;
十位上的8代表的值其实是8×,即80;
百位的1同理代表1×,即100;
可见不同数位上,即便数字相同,其所代表的值也不同,这就是权重的含义。
再以一个二进制数1111为例:
最低位的1代表的值是1×,即1;
第二位的1代表的是1×,即2;
第三位的1代表的值是1×,即4;
第四位的1代表的值是1×,即8;
同样是1,但因为所处数位不同,其所代表的数值也不同。
要将上述的1111转化为十进制很简单:
首先如上计算出各个数位上的数的真实数值。
二进制不像我们熟悉的十进制那么直观。比如186这个数,十位上的8我一眼就能知道它代表的是80;其他进制则需要我们通过计算得到各个数位上真实的值,如二进制下1111的四个1真实数值分别为1,2,4,8;
再求和即得其十进制的表示法:1+2+4+8=15;
二进制的1111和十进制的15在数值上其实是一样的,但是因为采用了不同的进制所以表示方法不同;
1×+1×+1×+1×=1×+5×
从上例我们就可以看出将任意进制转化为10进制的方法;
根据每个数位的权重,算出该数位上的数实际表示的值,然后求和;
比如十六进制数1E(十六进制中E表示14):
=1×+14×=;
可以发现,在任一进制下,随着数位的增加,其权重必然也会增加。而不同进制的区别可能就在于,它们权重随数位的递增量不同。
下附十六进制转十进制代码:
long long base_conversion(char s[]){
long long base=1;
long long ret=0;
for(int i=strlen(s)-1;i>=0;i--){
if(s[i]>='A'&&s[i]<='F')
ret+=((s[i]-'A'+10)*base);
else
ret+=(s[i]-'0')*base;
base*=16;
}
return ret;
}
int main()
{
char s[10];//因为十六进制数可能含A~F,所以用字符串来存储十六进制数
cin>>s;
cout<<base_conversion(s);
return 0;
}
二.10进制转换为任意进制
除数取余法(模n取余法):
以将十进制的12转化为二进制为例:
1) 12%2=0 ………………0
2)12/2=6 6%2=0 ………………0
3) 6/2=3 3%2=1 ………………1
4) 3/2=1 1%2=1 ………………1
5) 1/2=0
所以12的二进制表示就是1100
将十进制的30转化为八进制:
1) 30%8=6………………6
2)30/8=3 3%8= 3………………3
3)3/8=0
所以30的八进制表示就是36
原理:
一个任意的整数 都可以表示以10为底的幂组成的多项式:
例:386=3×+8×+6×; 42=4×+2×;
不难发现,10的k次幂前的系数就是原数第k+1位上的数
而以386=3×+8×+6×=10×(3×+8)+6为例
可以看到我们对386除以10后得到的余数就是其个位上的数字6
因为除了以6为系数的这一项,其他项起码含一个10的因子。
根据这个定理,我们只要对一个整数不断地除以10,然后用商做下一次运算的被除数就可以得到这个整数个位,十位,百位……上的数字
同样的,一个任意的整数 也可以表示为以2为底的幂组成的多项式:
例:137=1×+0×+0×+0×+1×+0×+0×+1×
由这个多项式可直接得到137的二进制表示:10001001
和10进制一样,除了最低位,其他项都是2的倍数,我们对137除以2得到的余数就是其最低位上的数字。
所以我们只要对一个整数除以2取余数,再不断取商做下一次运算即得其二进制表示;
其他进制也都是一样的(下以十进制转十六进制为例):
#include<bits/stdc++.h>
using namespace std;
int main()
{
int x,cnt=0;
char ans[10];
cin>>x;
while(x){
int temp=x%16;
if(temp<=9)
ans[cnt++]=temp+'0';
else
ans[cnt++]=temp-10+'A';
x/=16;
}
if(cnt==0)
cout<<'0';
for(int i=cnt-1;i>=0;i--)
cout<<ans[i];
return 0;
}
三.大数任意进制的转换
任意进制转换其实很简单,因为我已经写了任意进制转十进制以及十进制转任意进制的办法;所以我们只要以十进制为中介,就能实现任意进制的转换。
难点就在大数上。
当一个数字过大时,C语言中我们只能用字符串来存储它。
这在计算上带来了很大的麻烦。
一个任意进制转换为十进制需要大量的大数乘法和大数求和;
而十进制转换为任意进制更难,需要做很多大数的取模运算。
我们可以使用高精度计算来解决,在某些情况下却也有更简便的方法。
下以十六进制转换为八进制来举例:
在将十六进制转换为八进制时,我们可以用二进制作为媒介,它相比之前讲过的方法更简单。
1)将十六进制转换为二进制
将每个十六进制的数,转换为一个四位的二进制数即可。
比如7转换为0111,A转换为1010。
那么十六进制的7A转换为二进制就是01111010,再去掉前导0即可。
2)将二进制转换为八进制
从低位开始,不断取三位二进制数合成一个八进制数,其实相当于上述运算的逆运算。
比如上面的1111010:
先取低位的三个数字010,转换为八进制即2。
再取111,转换为八进制即7。
最后不足三位了,只剩一个1,看成001即可,转换为八进制即1。
结果:172。
经验算可得十六进制的7A,二进制的1111010,八进制的172表达的数值都是相同的,即十进制的122。
下附代码(用到了双向队列deque):
#include <bits/stdc++.h>
using namespace std;
int main()
{
string s;
cin>>s;//读入十六进制数
deque<int>q,ans;
for(int i=0;i<s.size();i++){//逐位计算
int temp;//temp用于计算每位十六进制数对应的十进制表达
if(s[i]>='0'&&s[i]<='9') temp=s[i]-'0';
else temp=s[i]-'A'+10;
//再将十进制转换为二进制,模二取余法即可
int a[4];
for(int cnt=3;cnt>=0;cnt--){
a[cnt]=temp%2;
temp/=2;
}
//再将结果存入双向列表q中
for(int i=0;i<4;i++)
q.push_back(a[i]);
}
//以上实现了十六进制到二进制的转换
while(!q.empty()){
int num;//num表示要取的个数
q.size()<3?(num=q.size()):(num=3);
//最后不足三位时,还剩几位取几位
int temp=0,t=1;
for(int i=0;i<num;i++){
temp+=(q.back()*t);
q.pop_back();
t*=2;
}
//用temp存储,转化为了八进制
ans.push_front(temp);//存入答案
}
if(ans.front()==0)//如果含有前导0要去掉
ans.pop_front();
while(!ans.empty()){
cout<<ans.front();
ans.pop_front();
}
cout<<endl;
}
return 0;
}