鱼C论坛

 找回密码
 立即注册
查看: 2111|回复: 10

[技术交流] C++进制与进制转换方法讲解 超详细

[复制链接]
发表于 2022-8-31 14:24:20 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 zhangjinxuan 于 2022-8-31 19:53 编辑

在开始之前,请让我啰嗦两句什么是进制(明白的鱼油跳到1的位置)
进制也就是进位计数制,是人为定义的带进位的计数方法(有不带进位的计数方法,比如原始的结绳计数法,唱票时常用的“正”字计数法,以及类似的tally mark计数)。 对于任何一种进制---X进制,就表示每一位上的数运算时都是逢X进一位。 十进制是逢十进一,十六进制是逢十六进一,二进制就是逢二进一,以此类推,x进制就是逢x进位(这么专业的语言,肯定不是我写的 )

可能还有的鱼油很懵逼,专业的语言听不懂!那用我的话说吧:
进制决定了一个数能表示哪些数字,比如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进制让我们快乐学习呢?
其实计算机很'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

讲了这么多,相信大家都会了,现在,上代码!

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. int pp(string number)  //返回number中点号的位置
  4. {
  5.         int len=number.size();
  6.         for (int i=0;i<len;++i)
  7.                 if (number[i]=='.')
  8.                         return i;
  9.         return len;
  10. }
  11. double p1(string number,int base)
  12. {
  13.         double res=0;
  14.         int len=number.size(),ppos=pp(number),t=1;
  15.         for (int p=ppos-1;p>=0;--p) //整数转换--从左往右看
  16.         {
  17.                 res+=(number[p]>='A'?number[p]-'A'+10:number[p]-'0')*t;
  18.                 t*=base;
  19.         }
  20.         if (ppos==len)return res;
  21.         else
  22.         {
  23.                 double t=base;
  24.                 for(int p=ppos+1,i=-1;p<len;++p,--i)//小数转换--从右往左看
  25.                 {
  26.                         res+=(number[p]>='A'?number[p]-'A'+10:number[p]-'0')*(1/t);//倒数
  27.                         t*=base;
  28.                 }
  29.                 return res;
  30.         }
  31. }
  32. int main()
  33. {
  34.         string number;
  35.         int base;
  36.         cin>>number>>base;
  37.         printf("%g",p1(number,base));
  38. }
复制代码


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(代表进制)取整法,这是啥意思啊 ,就是乘一次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进制,分开转换再相加即可。

上代码加深鱼油们的理解:
  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. string ip2(int number,int base,string res) //整数部分的转换,因为是逆序输出,所以我想到了栈,因为我想到了栈,所以我想到了递归
  4. {
  5.         if (number<base)return char(number>9?number-10+'A':number+'0')+res;
  6.         return ip2(number/base,base,char(number%base>9?number%base-10+'A':number%base+'0')+res);  //仔细回想方法,读完这句并不难
  7. }
  8. string fp2(double number,int base) //小数转换,这就没必要用递归了
  9. {
  10.         int c=0;//为防止死循环,定义一个c变量
  11.         string res;
  12.         while (number && c!=50)//最多保留50位小数
  13.         {
  14.                 number*=base;//乘x
  15.                 res=res+char(int(number)>9?number-10+'A':number+'0');//取整
  16.                 ++c;
  17.                 number=number-int(number);//保留小数
  18.         }
  19.         return res;
  20. }
  21. string p2(double number,int base)
  22. {
  23.         string tmp = ip2(int(number),base,"");
  24.         if (int(number)!=number)tmp=tmp+"."+fp2(number-int(number),base);
  25.         return tmp;
  26. }
  27. int main()
  28. {
  29.         double x;
  30.         int base;
  31.         scanf("%lf%d",&x,&base);
  32.         cout<<p2(x,base);
  33. }
复制代码

实际上,在C++中,有10进制转X进制的函数,那就是atoi,但只能进行整数的转换
用法:
  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. int main()
  4. {
  5.     char res[100];
  6.     atoi(6666,res,16);
  7.     printf("%s",res);
  8. }
复制代码

程序输出:
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.广告:求评分回帖加顶

[本文为本萌新原创,仅学习使用,若有不正请大佬指正,有YB!]
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2022-8-31 15:02:32 | 显示全部楼层

回帖奖励 +1 鱼币

可以
这个帖子很精美吖

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-8-31 15:24:10 | 显示全部楼层
有点难!
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2022-8-31 15:26:55 | 显示全部楼层

是有点难,我尽量让大家明白,关于进制我也是造了十多天
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-8-31 16:10:57 | 显示全部楼层

回帖奖励 +1 鱼币

很详细,不过多少有些吹毛求疵,并不是错误:转 10 进制时 pow 用的有点过多了,做了一些不必要的计算。尽管这样处理的数长度不会太长,这点多余计算可能不会导致显著的差异,也可以考虑一下迭代的方法,比如
  1. #include <iostream>
  2. #include <string>
  3. using namespace std;
  4. inline unsigned int toInteger(char ch){ return ch >= 'A' ? ch - 'A' + 10 : ch - '0'; }
  5. double p1(const string& number,int base)
  6. {
  7.         #if false
  8.         // approach #1
  9.         double res = 0.0;
  10.         bool dot_passed = false;
  11.         double current_index = 1.0;
  12.         for(const auto c: number){
  13.                 if(c == '.'){ dot_passed = true; continue; }
  14.                 if(dot_passed) res += toInteger(c) * (current_index /= base);
  15.                 else res = res * base + toInteger(c);
  16.         }
  17.         return res;
  18.         #else
  19.         // approach #2
  20.         double result_part_1 = 0.0;
  21.         double result_part_2 = 0.0;
  22.         bool existing_dot = false;
  23.         for(auto iter = number.cbegin(); iter != number.cend(); ++iter){
  24.                 if(*iter == '.') { existing_dot = true; break; }
  25.                 result_part_1 = result_part_1 * base + toInteger(*iter);
  26.         }
  27.         if(existing_dot) for(auto iter = number.crbegin(); iter != number.crend(); ++iter){
  28.                 if(*iter == '.') break;
  29.                 result_part_2 = (result_part_2 + toInteger(*iter)) / base;
  30.         }
  31.         return result_part_1 + result_part_2;
  32.         #endif
  33. }
  34. int main()
  35. {
  36.         string number;
  37.         int base;
  38.         cin>>number>>base;
  39.         printf("%g",p1(number,base));
  40. }
复制代码

没做多少测试,如果有问题欢迎指出,也欢迎写法改进。

评分

参与人数 1鱼币 +1 收起 理由
zhangjinxuan + 1 .

查看全部评分

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2022-8-31 16:17:50 | 显示全部楼层
dolly_yos2 发表于 2022-8-31 16:10
很详细,不过多少有些吹毛求疵,并不是错误:转 10 进制时 pow 用的有点过多了,做了一些不必要的计算。尽 ...

pow写的是很多余,学到了,但我追求简洁,复杂的怕大家看不懂。明天给你评分,今天没机会了
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2022-8-31 16:18:55 | 显示全部楼层
对了,创作不易,如果学到了求评分~
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2022-8-31 16:25:41 | 显示全部楼层
我研究研究。。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-8-31 16:30:16 | 显示全部楼层
zhangjinxuan 发表于 2022-8-31 16:17
pow写的是很多余,学到了,但我追求简洁,复杂的怕大家看不懂。明天给你评分,今天没机会了

确实,迭代尽管效率上可能会有微小提高但牺牲了直观和易理解的特性。您考虑的思路很清晰,比我高明。

评分

参与人数 1荣誉 +1 收起 理由
zhangjinxuan + 1 鱼C有你更精彩,明天给你加YB

查看全部评分

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2022-8-31 16:34:14 | 显示全部楼层
已修改,谢谢大佬!
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-8-31 17:15:55 | 显示全部楼层
赞!感谢楼主分享~
支持原创!
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-5-11 17:16

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表