描述
十进制整数N和其他D(D的值为2、8、16)进制数的转换是计算机实现计算的基本问题,其解决方法很多,其中一个简单算法基于下列原理:N = (N div d)×d + N mod d (其中:div 为整除运算,mod 为求余运算)。简单来说,就是除D取余,然后倒过来得到D进制的数。
例如:
(1348)10 = (10101000100)2
(1348)10 = (2504)8
(1000)10 = (3E8)16
(请注意:转16进制时,用A代表余数10,B代表余数11……)
假设现要编制一个满足下列要求的程序:对于输入的任意一个非负十进制整数(n<=1,000,000,000),打印输出与其等值的D进制数。
输入描述
有两个整数N和D,N表示要转换的十进制非负整数,D代表要转换的进制(2、8或16)
输出描述
N进制转D进制的结果
用例输入 1
1348 2
用例输出 1
10101000100
分析
(此题需要理解二进制,八进制,十进制,十六进制)
1、二进制与十进制相互转换
二进制转为十进制
0000 0110转换为10进制:
(二进制里面没有"个位、十位、百位",只能通过从左到右或者从右到左第几位来描述),
从右往左开始, 第一位是0,进制的基数是2,那么就是0 * 20 ,第二位是1,就是1 * 21 ,
第三位是1,就是1 * 22,第四位及以上都是0了,那么不必再计算,于是0000 0110转换成十进制是0 * 20+1 * 21+1 * 22=6;0000 0101转换成十进制是5。
八进制是利用数位上的值乘以进制基数(8)的幂次方来转换,十六进制是利用数位上的值乘以进制基数(16)的幂次方来转换
1)十进制转为二进制
1.十进制48转换成二进制
因为是转换成二进制。所以用48除以进制基数2,直到结果为1(为什么说直到结果为1,因为不管任何数,按照上面的演算方式不断除以2,最后的结果一定是1),然后将结果的1放在最前面,后面依次写上每一步的余数,注意,这里每一步的余数是倒序(也就是从下往上排列),也就是说排在结果1后面的余数是计算过程3/2的余数,然后是计算过程6/2的的余数…所以最后得出十进制数48的二进制表示是110000。如果是byte类型,需要在前面补0,直至8位:0011 0000,如果是int类型就是:0000 0000 0000 0000 0000 0000 0011 0000。
2.十进制550转换成二进制:
结果为:10 0010 0110,如果是int类型,则补齐32位,结果是:0000 0000 0000 0000 0000 0010 0010 0110。
实际上,二进制为什么需要从最后的余数开始,你们仔细思考一下:是不是和二进制转换成十进制的时候,进制基数2的幂次方是从0开始的有关。
还有一个问题需要强调,就是为什么上面表格中的奇数除以2,不会出现浮点数,这是因为,上面的除法都是整数类型,不涉及浮点数类型,所以,整数类型的除法结果都是整数,直接舍弃了小数部分,所以31除以2,结果是15,而不会是15.5。
2、八进制与16进制表示
八进制在编程语言范围内没有固定的使用情形,它的基数是8,总共有8个数字符号(0,1,2,3,4,5,6,7),八进制的最大数码是基数减1,就是7,最小数码是0,如果你要确切表示一个数是八进制的,可以这么表示(12565)O或者是(12565)Q,在C和C++中八进制的表示是额外在数值前面加一个0,比如123是十进制,而0123就是八进制。
十六进制在编程语言范围内也没有固定的使用情形(计算机网络中最新的IPv6地址使用的就是十六进制,计算机系统的注册表也会用到),它的基数是16,总共有16个数字符号(0,1,2,3,4,5,6,7,8,9,A[表示10],B[表示11],C[表示12],D[表示13],E[表示14],F[表示15]),因为0-9不够用,所以就借了6个字母,字母不区分大小写,对比前面几种进制,只要一个数的表示中出现了字母,就一定是16进制。十六进制的最大数码也是基数减1,就是15(F),最小数码也是0,如果你需要确切表示一个数是十六进制的,可以这么表示(56BBA)H,在C和C++中,十六进制的表示是额外在数值前面加一个0x,比如123是十进制,0x123是十六进制。
2)1.八进制转换成十进制 : 十进制48转换位八进制
结果为60,这里需要特别注意的是,千万不要受二进制的影响,非要得到结果为1,这里不可能为1,因为进制基数变成了8,所以,48/8得出的结果是6,已经比进制基数8更小了,就没有再计算下去的必要(因为再计算下去就是6/8,结果是0了),于是从结果6开始,倒序排列各步骤的余数,得到的结果就是60(10进制转换成8进制的时候,一旦得到的结果比8更小,则说明是最后一步了)。
2.十进制360转换为八进制表示:
结果5比进制基数8小,所以结果就是550。
3.十六进制转换为十进制:十进制48转换位十六进制的表示:
十六进制与8进制一样,只要得到的结果比进制基数更小,则停止运算,所以结果是30。
4.十进制100转换位十六进制的表示:
结果为:65。
3、二、八、十六进制间的相互转换
二进制转换成八进制
只需要将二进制的表示从右往左开始,每三位二进制数为1组 ,分到最后如果不足3位,那么剩下多少位就是多少位,再用每组的二进制的每一位数从右往左依次乘以20、21、22,然后相加,得出一组的结果,最后将所有组的结果相连,得出最终的结果(这里注意了,二进制转换为八进制的时候是分组了,并且最后是将每组的结果相连,而不是相加)。
二进制(0011 0101)B转换为八进制
表示是什么结果:
首先,将二进制从右至左进行分组:
分别是 第一组:101 第二组:011 第三组:00。实际上,第三组没意义了,因为都是0,我们来关注前两组
第一组计算过程是:1 * 20+0 * 21+1 * 22=5;
第二组计算过程是:0 * 20+1 * 21+1 * 22=6;
所以最后的结果是65。也就是用6和5直接相连,而不是相加,这里还要注意一下相连的顺序问题,是6–5的方向。
二进制转换为十六进制
二进制转换为十六进制就是将二进制每四位二进制为一组,其他与八进制转换为二进制一样。
八进制转换为二进制
只需要将八进制的每一个数用三位二进制表示,然后相连既可以。
十六进制转换为二进制
只要需要将十六进制的每一个数用四位二进制表示,然后相连即可。
八进制转换为十六进制
不要以为八和十六之间存在倍数2的关系就有什么捷径,实际上没有,需要通过二进制中转一下。
所以需要先将八进制转换成二进制,在转换成十六进制。
十六进制转换成八进制
需要将十六进制转换成二进制,再将二进制转换成八进制。
(1)原码是符号位加上真值的绝对值,即用第一位表示符号位,其余位表示值。
(2)正数的反码是其本身。负数的反码是在原码的基础上,符号位不变,其余按位取反。
(3)正数的补码是其本身。负数的补码是在原码的基础上,符号位不变,其余按位取反,最后加1。
(4)计算机进行符号运算时,会以补码表示,符号位和数字位会一起参与运算。
a-b实际转换为[a-b]补=[a]补+[-b]补
a为1111(后面60个0),1开头,所以为一个负数;补码为1001(后面60个0)
b为0111(后面60个1),0开头,所以为一个正数;补码为其本身。
-b为1111(后面60个1),补码为1000(后面59个0,最后有个1)
所以1001(后面60个0) + 1000(后面59个0.最后有个1) = 0001(后面59个0,最后有个1),发生了溢出,比如采用双高位判别法处理溢出,则补码1001(后面59个0,最后有个1)转换为原码为1110(后面60个1),即-(7 *2的60次方+1)=-(2的62次方+2的61次方+2的60次方 + 1)
(有亿点点小长,请耐心观看)
代码
#include<bits/stdc++.h>
using namespace std;
int main() {
int n,t;
char c;
string s;
cin>>n>>t;
while(n!=0) {
if(t==16) {
if(n%16>=10) {
c=n%16-10+'A';
} else {
c=n%16+'0';
}
}else{
c=n%t+'0';
}
s=c+s;
n=n/t;
}
if(s=="") {
cout<<0;
} else {
cout<<s;
}
return 0;
}