C++进制与进制转换方法讲解 超详细
本帖最后由 zhangjinxuan 于 2022-8-31 19:53 编辑在开始之前,请让我啰嗦两句什么是进制(明白的鱼油跳到1的位置)
进制也就是进位计数制,是人为定义的带进位的计数方法(有不带进位的计数方法,比如原始的结绳计数法,唱票时常用的“正”字计数法,以及类似的tally mark计数)。 对于任何一种进制---X进制,就表示每一位上的数运算时都是逢X进一位。 十进制是逢十进一,十六进制是逢十六进一,二进制就是逢二进一,以此类推,x进制就是逢x进位(这么专业的语言,肯定不是我写的 )
{:5_109:}
可能还有的鱼油很懵逼,专业的语言听不懂!那用我的话说吧:
进制决定了一个数能表示哪些数字,比如10进制,可以表示0~9的数字,如果要表示更大的数字那就要进位 比如:9+2=11。
换个例子,比如8进制,可以表示0~7的数字,要表示更大的数同样要进位,比如6+2在8进制中不能等于8,应向前进一,最后6+2在八进制中等于10。
进制的表示方法很简单,比如一个b进制数x,表示为(x)b(应在数字两边打括号,再把进制写在右括号的右下角),比如2进制数1010,表示为(1010)2
常用的进制有:2进制,8进制,10进制(这就是我们刷数学题用的进制),16进制,有人可能会说:我们10进制用的好好的,干嘛要发明2,8,16进制让我们快乐学习呢?{:10_277:}
其实计算机很'2',它只能表示0,1,就是二进制,计算机的地址使用16进制表示的,C++/C也可以用8进制表示数字。为什么8,16进制与计算机关系这么密切?
因为计算机很‘2’,它喜欢和2有关的,8正好就是2的3次方,16正好是2的4次方,所以,学习进制还是很重要的。
回归正题,我们来学学怎么实现不同进制间的转换吧!
1.X进制转10进制:
用的是按位展开求和的方法,什么意思呢?想一想我们可以怎样表示一个10进制数
比如(34201)10 怎么表示?聪明的鱼油会这么表示:34201=3*10000+4*1000+2*100+0*10+1
对,这么表示正确,但不够一目了然,可以这么表示:34201=3*10^4 + 4*10^3 + 2*10^2 + 0*10^1 +1*10^0 (^为求幂)
可能聪明的鱼油明白按位展开求和是什么意思了,所以我们看看是不是对2进制,或别的进制有效:
(10)2=1*2^1+0*2^0=(2)10 检验一下:2在10进制中等于1+1,而1+1在2进制中就是(10)2
可能还是有的鱼油很懵,没事,很正常,我们来看几个例子:
(1101)2=1*2^3+1*2^2+0*2^1+1*2^0=8+4+1=(13)10
(235)8=2*8^2+3*8^1+5*8^0=128+24+5=(157)10
(404)5=4*5^2+0*5^1+4*5^0=100+0+4=(121)10
(ABC)16 我的天哪,ABC是什么鬼?其实上,为了表示16进制或更多的进制的数,就只能用英文字母表示了(可用大写、小写,我喜欢用大写)
A对应的是10,B对应的是11,C对应的是12,D对应的是13,E->14,F->15,......
所以不难计算出(ABC)16=10*16^2+11*16^1+12*16^0=2560+176+12=(2748)10
小数怎么转换呢?同样也是按位展开求和,但得是负数,比如这样:
(0.22)3=2*3^-1+2*3^-2≈(0.889)10
整数部分与小数部分分开转换,最后加起来就可以了!
有的人又说3^-1我不会算!其实很简单:不看负号,算3^1=3,最后求出倒数就可以了(x的倒数是1/x)
举个例子(13A.B3A)16=1*16^2+3*16^1+10*16^0+11*16^-1+3*16^-2+10*16^-3=(314.70166015625)10
讲了这么多,相信大家都会了,现在,上代码!
#include <bits/stdc++.h>
using namespace std;
int pp(string number)//返回number中点号的位置
{
int len=number.size();
for (int i=0;i<len;++i)
if (number=='.')
return i;
return len;
}
double p1(string number,int base)
{
double res=0;
int len=number.size(),ppos=pp(number),t=1;
for (int p=ppos-1;p>=0;--p) //整数转换--从左往右看
{
res+=(number>='A'?number-'A'+10:number-'0')*t;
t*=base;
}
if (ppos==len)return res;
else
{
double t=base;
for(int p=ppos+1,i=-1;p<len;++p,--i)//小数转换--从右往左看
{
res+=(number>='A'?number-'A'+10:number-'0')*(1/t);//倒数
t*=base;
}
return res;
}
}
int main()
{
string number;
int base;
cin>>number>>base;
printf("%g",p1(number,base));
}
2. 10进制转x进制:
使用除x取余法,什么意思呢?就是不停把除x的余数保存,再/=x,我们看个例子:
12转为2进制是多少?
step 1: 12/2=6 余数为0
step 2: 6/2=3 余数为0
step 3: 3/2=1 余数为1
step 4: 1/2=0 余数为1
step 5: 结果已为0,无需进行计算,最后答案倒序输出
所以 (12)10=(1100)2
这就是除x取整法,比按位展开求和算起来简单许多
再看个例子:
6666转16进制是多少?
step 1:6666/16=416 余数为 10
step 2:416/16=26 余数为 0
step 3:26/16=1 余数为10
step 4:1/16=0 余数为 1
step 5:结果已为9,无需进行计算,最后答案倒序输出,注:大于等于10的数字要转换成英文字母,比如10转换为A,11转换为B,14转换为E
所以(6666)10=(1A0A)16
[大家扣一波 1A0A]
对于小数,那就用乘x(代表进制)取整法,这是啥意思啊{:10_291:} ,就是乘一次x,把结果的整数部分保存,把整数部分变为零,直到x变为0, 我们举个栗子:
0.125转为2进制是多少?
step 1:0.125*2=0.25 整数部分为 0
step 2:0.25*2=0.5 整数部分为 0
step 3:0.5*2=1 整数部分为1,然后把整数部分变为0(每乘一次记录整数部分,再把整数部分变为0,然后继续乘)
step 4:如果结果变成0,就不除了,最后按顺序输出
所以(0.125)10=(0.001)2
注:不一定所有的数都能转换,有时候会转换成无限小数,大家可以试一试0.3转2进制
再看个例子,加深大家的理解:
0.32转8进制约是多少?
step 1: 0.32*8=2.56整数部分为 2
step 2: 0.56*8=4.48整数部分为 4
step 3: 0.48*8=3.84整数部分为 3
step 4: 0.84*8=6.72整数部分为 6
step 5: 0.72*8=5.76整数部分为 5
...
所以(0.32)10≈(0.24365)8
大多数字转换别的进制数都会变为无限小数,所以一般算6位左右就差不多了
如果对于既有整数又有小数的数字,比如31.41转8进制,分开转换再相加即可。
上代码加深鱼油们的理解:
#include <bits/stdc++.h>
using namespace std;
string ip2(int number,int base,string res) //整数部分的转换,因为是逆序输出,所以我想到了栈,因为我想到了栈,所以我想到了递归
{
if (number<base)return char(number>9?number-10+'A':number+'0')+res;
return ip2(number/base,base,char(number%base>9?number%base-10+'A':number%base+'0')+res);//仔细回想方法,读完这句并不难
}
string fp2(double number,int base) //小数转换,这就没必要用递归了
{
int c=0;//为防止死循环,定义一个c变量
string res;
while (number && c!=50)//最多保留50位小数
{
number*=base;//乘x
res=res+char(int(number)>9?number-10+'A':number+'0');//取整
++c;
number=number-int(number);//保留小数
}
return res;
}
string p2(double number,int base)
{
string tmp = ip2(int(number),base,"");
if (int(number)!=number)tmp=tmp+"."+fp2(number-int(number),base);
return tmp;
}
int main()
{
double x;
int base;
scanf("%lf%d",&x,&base);
cout<<p2(x,base);
}
实际上,在C++中,有10进制转X进制的函数,那就是atoi,但只能进行整数的转换
用法:
#include <bits/stdc++.h>
using namespace std;
int main()
{
char res;
atoi(6666,res,16);
printf("%s",res);
}
程序输出:
1a0a
3 x进制数a 转y进制
虽然很简单,但算起来特别难
方法:把a转换为10进制数b,用按位展开求和的方法,然后把b转换为y进制,用除x取整/乘x取整的方法。
比如(1010)2转8进制:
先转10进制:1*2^3+1*2**1=8+2=10
再转8进制:
10/8=1 余2
1/8=0 余1
结果为12,代码很简单,可以自己写^_^
4.不同进制间的运算
先一起转10进制,接着按要求进行运算,再转为要求的进制
5.广告:求评分回帖加顶{:10_254:} !
[本文为本萌新原创,仅学习使用,若有不正请大佬指正,有YB!] 可以
这个帖子很精美吖{:10_256:}
有点难! jhanker 发表于 2022-8-31 15:24
有点难!
是有点难,我尽量让大家明白,关于进制我也是造了十多天 很详细,不过多少有些吹毛求疵,并不是错误:转 10 进制时 pow 用的有点过多了,做了一些不必要的计算。尽管这样处理的数长度不会太长,这点多余计算可能不会导致显著的差异,也可以考虑一下迭代的方法,比如
#include <iostream>
#include <string>
using namespace std;
inline unsigned int toInteger(char ch){ return ch >= 'A' ? ch - 'A' + 10 : ch - '0'; }
double p1(const string& number,int base)
{
#if false
// approach #1
double res = 0.0;
bool dot_passed = false;
double current_index = 1.0;
for(const auto c: number){
if(c == '.'){ dot_passed = true; continue; }
if(dot_passed) res += toInteger(c) * (current_index /= base);
else res = res * base + toInteger(c);
}
return res;
#else
// approach #2
double result_part_1 = 0.0;
double result_part_2 = 0.0;
bool existing_dot = false;
for(auto iter = number.cbegin(); iter != number.cend(); ++iter){
if(*iter == '.') { existing_dot = true; break; }
result_part_1 = result_part_1 * base + toInteger(*iter);
}
if(existing_dot) for(auto iter = number.crbegin(); iter != number.crend(); ++iter){
if(*iter == '.') break;
result_part_2 = (result_part_2 + toInteger(*iter)) / base;
}
return result_part_1 + result_part_2;
#endif
}
int main()
{
string number;
int base;
cin>>number>>base;
printf("%g",p1(number,base));
}
没做多少测试,如果有问题欢迎指出,也欢迎写法改进。 dolly_yos2 发表于 2022-8-31 16:10
很详细,不过多少有些吹毛求疵,并不是错误:转 10 进制时 pow 用的有点过多了,做了一些不必要的计算。尽 ...
pow写的是很多余,学到了,但我追求简洁,复杂的怕大家看不懂。明天给你评分,今天没机会了 对了,创作不易,如果学到了求评分~ 我研究研究。。 zhangjinxuan 发表于 2022-8-31 16:17
pow写的是很多余,学到了,但我追求简洁,复杂的怕大家看不懂。明天给你评分,今天没机会了
确实,迭代尽管效率上可能会有微小提高但牺牲了直观和易理解的特性。您考虑的思路很清晰,比我高明。 已修改,谢谢大佬! 赞!感谢楼主分享~
支持原创!
页:
[1]