十进制、二进制、八进制、十六进制对应表
十进制 | 二进制 | 八进制 | 十六进制 |
---|---|---|---|
00 | 0000 | 00 | 0 |
01 | 0001 | 01 | 1 |
02 | 0010 | 02 | 2 |
03 | 0011 | 03 | 3 |
04 | 0100 | 04 | 4 |
05 | 0101 | 05 | 5 |
06 | 0110 | 06 | 6 |
07 | 0111 | 07 | 7 |
08 | 1000 | 10 | 8 |
09 | 1001 | 11 | 9 |
10 | 1010 | 12 | a |
11 | 1011 | 13 | b |
12 | 1100 | 14 | c |
13 | 1101 | 15 | d |
14 | 1110 | 16 | e |
15 | 1111 | 17 | f |
二进制转十进制
(10111011)2 = ( ? )10
1 x 28-1 + 0 x 27-1 + 1 x 26-1 + 1 x 25-1 + 1 x 24-1 + 0 x 23-1 + 1 x 22-1 + 1 x 21-1 = 128 + 0 + 32 + 16 + 8 + 0 + 2 + 1 = (187)10
#include <stdio.h>
#include <math.h>
int main(){
int a;
int sum = 0;
int c = 0;
scanf("%d",&a);
for(; a != 0; a /= 10){
sum += (a % 10) * pow(2, c++);
}
printf("%d",sum);
return 0;
} //注意输入的二进制数的范围限制,int型存储最大整数为2147483647
//所以输入的最大二进制数为1111111111
//该段代码功能:
//将二进制数转换为十进制,输入的二进制数最大为31个1
//因为int型存储范围为2的31次方减1
//如果输入了0和1以外的数字,提示输入错误重新输入
#include <stdio.h>
#include <math.h>
int main(){
int er[31]; //将二进制数存入数组,因为C语言中没有任何数据类型能够存放31个1这么大的数
int i, j, count, k, m, flag, a, sum;
char digit; //digit接收输入的字符
printf("请输入一个二进制数(该二进制数最大为1111111111111111111111111111111):\n");
do{ //do-while循环实现输入一个二进制数的功能,如果输入了0和1以外的数字,提示输入错误重新输入
flag = count = 0; //count统计输入二进制的位数
for (i = 0; i <= 31; i++){ //for循环接收来自键盘的输入
digit = getchar();
if (digit == '1'){ //输入的字符为1时
er[i] = 1;
count++; //count为二进制的位数
}
else if (digit == '0'){ //输入的字符为0时
er[i] = 0;
count++; //count为二进制的位数
}
else if (digit == '\n')
break; //输入了回车结束循环
else{
printf("输入错误!请重新输入!\n");
flag = 1; //输入了0和1以外的数字,提示输入错误,改变flag的值为1
}
}
} while (flag == 1); //flag的值为1,重新输入
sum = m = 0; //初始化sum和m,m作为数位,sum作为最后要输出的数
for (k = count - 1; k >= 0; k--){ //count-1倒着循环
sum = sum + er[k] * pow(2, m); //二进制转换十进制存入sum中
m++; //数位加1
}
printf("%d", sum);
return 0;
}
8421码和十进制之间的对应关系
8421码又称为BCD码(Binary Coded Decimal)
8 | 4 | 2 | 1 |
---|---|---|---|
23 | 22 | 21 | 20 |
1 | 1 | 1 | 1 |
(1111)2 = 8 + 4 + 2 + 1 = (15)10
十进制 | BCD码 |
---|---|
0 | 0000 |
1 | 0001 |
2 | 0010 |
3 | 0011 |
4 | 0100 |
5 | 0101 |
6 | 0110 |
7 | 0111 |
8 | 1000 |
9 | 1001 |
十进制转二进制
十进制转二进制——除二运算
#include <stdio.h>
int main(){
int a;
int b[31];
int i = 0, c = 0;
printf("请输入一个十进制数(该十进制数最大为2147483647): ");
scanf("%d",&a);
while(a != 0){
b[i++] = a % 2;
a /= 2;
c++;
}
for(c = c - 1; c >=0; c--)
printf("%d",b[c]);
return 0;
}
二进制、十进制互相转换(小数)
22 | 21 | 20 | . | 2-1 | 2-2 |
---|---|---|---|---|---|
1 | 0 | 1 | . | 1 | 1 |
(101.11)2 = 1 x 22 + 0 x 21 + 1 x 20 + 1 x 2-1 + 1 x 2-2 = 4 + 0 + 1 + 0.5 + 0.25 = (5.75)10
//将二进制小数转换为十进制数输出
#include <stdio.h>
#include <math.h>
int main(){
char a[20];
int b = 0;
double sum = 0;
for(int i = 0; i < 20; i++)
a[i] = 0; //初始化字符数组
printf("请输入一个二进制小数,位数限制在20位,小数点算一位: ");
while((a[b++] = getchar()) != '\n');
int c = 0;
for(int i = 0; a[i] != '.'; i++) //统计点之前的位数
c++;
int d = c - 1;
for(int i = 0; i < c; i++)
sum += (a[i] - '0') * pow(2, d--);
for(int i = c + 1; i < b - 1; i++)
sum += (a[i] - '0') * pow(2, d--);
printf("%.10f",sum);
return 0;
}
// 注意输入规范,注意范围限制
十进制小数转换为二进制:乘二运算,直到为1
(0.67578125)10 = ( ? )2
0.67578125 x 2 = 1.3515625 取个位数1··· ···0.1
0.3515625 x 2 = 0.703125 取个位数0··· ···0.10
0.703125 x 2 = 1.40625 取个位数1··· ···0.101
0.40625 x 2 = 0.8125 取个位数0··· ···0.1010
0.8125 x 2 = 1.625 取个位数1··· ···0.10101
0.625 x 2 = 1.25 取个位数1··· ···0.101011
0.25 x 2 = 0.5 取个位数0··· ···0.1010110
0.5 x 2 = 1 取各位数1··· ···0.10101101
(0.67578125)10 = (0.10101101)2
//将十进制小数转换为二进制小数
#include <stdio.h>
int main(){
double a;
scanf("%lf",&a);
int b;
int c;
int d = 0;
printf("0.");
while(a != 0){
c = a * 2;
a *= 2;
printf("%d",c);
if(c == 1)
a -= 1;
d++;
}
return 0;
}
由以上运行结果就可以看出,十进制小数与二进制小数的转换有时候并不是一定准确的,所以部分情况下,计算机在进行运算的时候会有一定的细微误差,这些细微误差可能会导致一些bug。一些无限循环的二进制小数在转换为十进制的时候,是做了一些近似处理的:
误差案例:
无论是C语言、Java还是Python,都会有这种小数细微误差的情况,本质上都是一样的,因为Java和Python的内核都是C或C++写的
十六进制、八进制、二进制互相转换
-
每四位二进制数对应一位十六进制数,四位四位拆分,左右两端位数不够补零:
(10101010001110.1011111111)2
= (0010 1010 1000 1110.1011 1111 1100)2
= (2 a 8 e.b f c)16
反过来,十六进制转二进制亦是如此 -
每三位二进制数对应一位八进制数,三位三位拆分,左右两端位数不够补零:
(10101010001110.1011111111)2
= (010 101 010 001 110.101 111 111 100)2
= (2 5 2 1 6.5 7 7 4)8
反过来,八进制转二进制亦是如此
//输入任意长度的二进制数,包括小数在内,将其转换为十六进制输出
//动态数组
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void Output(char* p, char* q);
void Transf(char* a);
int main(){
char getC;
int count = 0; //count统计字符的长度
char* p = NULL;
char* q = NULL;
char* m = NULL;
int flag = 0;
int n;
printf("请输入任意长度的二进制字符: \n");
while((getC = getchar()) != '\n')count++;
if((p = (char*)malloc(count)) == NULL){printf("内存申请失败!");exit(1);} //如果动态内存申请失败退出程序
printf("请再输入一遍: \n"); //第一遍输入统计长度分配动态存储空间,第二次输入存入数据
scanf("%s",p);
q = p;
for(; q < p + count; q++)
if(*q == '.'){ //判断是否输入了小数点
flag = 1;
m = q; //m纪录小数点的位置
}
if(flag == 0){ //若没有输入小数点
if(count % 4 == 0) //如果输入的二进制位数正好是4的倍数直接转换
Output(p, p + count);
else{ //不是4的倍数
int t = count % 4;
char* n = p - 4 + t;
for(; n < p; n++) //前面的位数补零
*n = '0';
Output(p - 4 + t, p + count);
}
}
else{ //若输入了小数点
if((m - p) % 4 == 0){ //小数点前面位数恰好为4的整数倍时
Output(p, m);
putchar('.');
}
else{
int t = (m - p) % 4;
char* n = p - 4 + t;
for(; n < p; n++) //前面的位数补零
*n = '0';
Output(p - 4 + t, m);
putchar('.');
}
if((p + count - m - 1) % 4 == 0) //小数点后面位数恰好为4的整数倍时
Output(m + 1, p + count);
else{
int t = (p + count - m - 1) % 4;
//printf("%d\n",t);
char* n = p + count;
for(; n < p + count + 4 - t; n++) //后面的位数补零
*n = '0';
Output(m + 1, p + count + 4 - t);
}
}
return 0;
}
void Output(char* p, char* q){
char a[4] = {'0','0','0','0'};
int i = 0;
for(; p < q; p++){
a[i++] = *p;
if(i == 4){
Transf(a);
i = 0;
}
}
}
void Transf(char* a){
if(strcmp(a, "0000")==0)printf("0");
else if(strcmp(a, "0001")==0)printf("1");
else if(strcmp(a, "0010")==0)printf("2");
else if(strcmp(a, "0011")==0)printf("3");
else if(strcmp(a, "0100")==0)printf("4");
else if(strcmp(a, "0101")==0)printf("5");
else if(strcmp(a, "0110")==0)printf("6");
else if(strcmp(a, "0111")==0)printf("7");
else if(strcmp(a, "1000")==0)printf("8");
else if(strcmp(a, "1001")==0)printf("9");
else if(strcmp(a, "1010")==0)printf("a");
else if(strcmp(a, "1011")==0)printf("b");
else if(strcmp(a, "1100")==0)printf("c");
else if(strcmp(a, "1101")==0)printf("d");
else if(strcmp(a, "1110")==0)printf("e");
else printf("f");
}
测试:经过验证,转换正确无误
二进制负数的反码与补码
原码:没经过处理的二进制码
反码:反码只针对负数,符号位不变,其他位取反,即0变成1,1变成0
补码:补码也只针对负数,负数的反码加1得到的二进制代码即为补码
[[X]反]反 = [X]原
[[X]补]补 = [X]补
[X]反+[Y]反 = [X + Y]反
[X]补+[Y]补 = [X + Y]补
之所以有反码和补码,是为了简化减法运算,使减法可以用加法电路实现。在计算机的内部,正数还是原码,而负数都以补码的形式存在,即将负数的原码取反后再加一。实际生活中的算术运算,比如(-7) + (+5)
,要考虑符号和绝对值大小的问题,这样就会非常麻烦,如果计算机电路也这样设计,就会出现许多复杂的步骤,为了简化计算机算术电路,所以负数采用补码。在计算机中,加、减、乘、除运算全部通过移位和加法来实现。
拿8位二进制数来说,最高位为符号位,最高位如果为1,则这个二进制数为负数,反之则为正数。例如:0000 0001
表示+1
,1000 0001
表示-1
,1000 0001
为-1
的原码,取反符号位不变,其他位取反1111 1110
,再加1得到补码1111 1111
,所以-1
在计算机中的代码为(以8位为例)1111 1111
,在计算机中计算1 - 1
实际上是0000 0001
加1111 1111
,进位舍去:
1 - 1
=0000 0001
+1111 1111
=0000 0000
4 - 2
=0000 0100
+1111 1110
=0000 0010
9 - 5
=0000 1001
+1111 1011
=0000 0100
43 - 75
= 00101011
+10110101
=11100000(-32的补码)
9 * 5
=1001
* 101
=
001001
+
00000
+
1001
=101101
=
00001001
* 00000101
=
00001001
+
00000000
+
00100100
=00101101