程序中进制的表示
二进制
二进制由 0 和 1 两个数字组成,使用时必须以0b或0B(不区分大小写)开头,例如:
//合法二进制
int a = 0b101; //换算成十进制5
int b = -0b110010; //换算成十进制-50
int c = 0B100001; //换算成十进制33
//不合法的二进制
int m = 101001; //无前缀0b或0B,相当于十进制
int n = 0B401; //4不是有效的二进制数字
请注意,标准的C语言并不支持上面的二进制写法,只是有些编译器自己进行了扩展,才支持二进制数字。换句话说,并不是所有的编译器都支持二进制数字,只有一部分编译器支持,并且跟编译器的版本有关系。
下面是实际测试的结果:
- Visual C++ 6.0 不支持。
- Visual Studio 2015 支持,但是 Visual Studio 2010 不支持;可以认为,高版本的 Visual Studio 支持二进制数字,低版本的 Visual Studio 不支持。
- GCC 4.8.2 支持,但是 GCC 3.4.5 不支持;可以认为,高版本的 GCC 支持二进制数字,低版本的 GCC 不支持。
- LLVM/Clang 支持(内嵌于 Mac OS 下的 Xcode 中)。
- window VScode支持
运算法则:逢二进一,借一当二
0B1101 - 0B0011 = 0B1010
0B1101 + 0B0011 = 0B10000
八进制
八进制由 0~7 八个数字组成,使用时必须以0开头(注意是数字 0,不是字母 o),例如:
//合法八进制 int a = 0105;
//换算成十进制69 0105 105
int b = -0101; //换算成十进制-65
int c = 017777; //换算成十进制8191
//不合法的八进制
int m = 256; //无前缀0,相当于十进制
int n = 0810; //8不是有效的八进制数字
运算法则:逢八进一,借一当八
0720 - 0101 = 0617
0325 + 0316 = 0643
十六进制
十六进制由数字 0~9、字母 A~F 或 a~f( 不区分大小写 A == 10 B==11 C==12 D==13 E==14 F==15)组成,使用时必须以0x或0X(不区分大小写)开头,例如:
//合法十六进制
int a = 0x2B; //换算成十进制43
int b = -0xA0; //换算成十进制160
int c = 0X70; //换算成十进制112
//不合法的十六进制
int m = 5A; //无前缀0X,是一个无效的数字
int n = 0x6H; //H一个无效的十六进制数字
运算法则:逢十六进一,借一当十六
0x71 - 0x2C = 0x45
0x88 + 0xA8 = 0x130
十进制
十进制由 0~9 十个数字组成,没有任何前缀,和我们平时的书写格式一样,不再赘述。
98 521 44
进制之间的转换
十进制转二进制(八进制、十六进制)
方法为:十进制数除2取余法。(转八进制时为除8取余法;转十六进制时除16取余),一直除,除到商为0时,反着取余数
例如:把十进制数 150 转换为 二进制数:如下:10010110
100化成二进制:
110 0100
权位值
2 进制
8192 4096 2048 1024 512 256 128 64 32 16 8 4 2 1
独特转化方法:将数字进行拆解,必须按最近最大权位值拆解,将有数字的位进行置即可。
100 = 64 + 32 + 4
64 32 16 8 4 2 1
1 1 0 0 1 0 0
819 = 512 + 256 + 32 + 16 + 2 + 1 转化二进制:1 1 0 0 1 1 0 0 1 1
二进制转十进制、八进制转十进制、十六进制转十进制
向十进制转时,将各位数按权展开,从右向左依次拿每位上的数乘以二(八 / 十六)的 0 1 2 ……次方,然后相加的和即是相应的十进制数。
权位值
2048 1024 512 256 128 64 32 16 8 4 2 1
独特转化方法:将对应的二进制权位值进行相加即可得对应的十进制数
1 1 0 1 0 1 化成十进制:32+16+4+1 = 53
32 16 8 4 2 1 (权位值)
1 1 0 1 0 1
二进制转八进制
方法为:每3位二进制数按权合成,然后相加得到1位八进制数。(注意事项,每3位二进制转成八进制是从右到左开始转换,不足时补0)。
001 101 001 011 010
010 = 2^1
011 = 2^1+2^0
第一步骤先分组:001 101 001 011 010
第二步,按权值合成八进制:0 15132
八进制转二进制
方法为:将八进制数的每一位通过除2取余法得到一个二进制数(也可以通过权值法),这个二进制数必须是3位,如果不够3 位,则最左补0。
值为:0226
0712 =0B111001010
0556=0B101101110
二进制转十六进制
与二进制转八进制相似,每四位按权相加得到一个数,从最右开始,最后若不足4位则向最左边补0,然后得到十六进制数。
0B1011011011110
步骤一:先分组(4位为一组): 0001 0110 1101 1110 = 0x1 6 D E
十六进制转二进制
方法为:将十六进制数的每一位通过除2取余法(也可以通过权值法),得到二进制数,每一位十六进制数得到的二进制数都应该是4位,不足时在最左边补零。
0xDD8 = 0B 1101 1101 1000
十六进制转八进制(八进制转十六进制)
方法为:借用一个中间进制,即先将十六进制(八进制)转换为二进制,然后再将二进制转换为八进制(十六进制)
0xA9F = 1010 1001 1111 (二进制) = 101 010 011 111 (二进制) = 0 5 2 3 7
分(二进制) 合(八进制)
0xE139A = 0B1110 0001 0011 1001 1010 = 011 100 001 001 110 011 010 = 03411632
进制之前转化总结
(1)记住二进制与十进制权位值关系
512 256 128 64 32 16 8 4 2 1
1 1 1 1 1 1 1 1 1 1
(2)进制之间转化要借助中间进制
十六进制转八进制借二进制, 十进制转八进制借用二进制,十六进制转十进制借二进制。
二进制数、八进制数和十六进制数的输出
C语言中常用的整数有 short、int 和 long 三种类型,通过 printf 函数,可以将它们以八进制、十进制和十六进制的形式输出。下表列出了不同类型的整数、以不同进制的形式输出时对应的格式控制符:
十六进制数字的表示用到了英文字母,有大小写之分,要在格式控制符中体现出来:
- %hx、%x 和 %lx 中的x小写,表明以小写字母的形式输出十六进制数;
- %hX、%X 和 %lX 中的X大写,表明以大写字母的形式输出十六进制数。
注意:八进制数字和十进制数字不区分大小写,所以格式控制符都用小写形式。如果你比较叛逆,想使用大写形式,那么行为是未定义的,请你慎重:
- 有些编译器支持大写形式,只不过行为和小写形式一样;
- 有些编译器不支持大写形式,可能会报错,也可能会导致奇怪的输出。
注意:虽然部分编译器支持二进制数字的表示,但是却不能使用 printf 函数输出二进制,这一点比较遗憾。当然,通过转换函数可以将其它进制数字转换成二进制数字,并以字符串的形式存储,然后在 printf 函数中使用%s输出即可。
eg:
#include <stdio.h>
int main()
{
short a = 0b1010110; //二进制数字
int b = 02713; //八进制数字
long c = 0X1DAB83; //十六进制数字
printf("a=%ho, b=%o, c=%lo\n", a, b, c); //以八进制形似输出 printf("a=%hd, b=%d, c=%ld\n", a, b, c); //以十进制形式输出 printf("a=%hx, b=%x, c=%lx\n", a, b, c); //以十六进制形式输出(字母小写) printf("a=%hX, b=%X, c=%lX\n", a, b, c); //以十六进制形式输出(字母大写)
return 0;
}
输出时加上前缀
区分不同进制数字的一个简单办法就是,在输出时带上特定的前缀。在格式控制符中加上#即可输出前缀,例如 %#x、%#o、%#lX、%#ho 等。
十进制数字没有前缀,所以不用加#。如果你加上了,那么它的行为是未定义的,有的编译器支持十进制加#,只不过输出结果和没有加#一样,有的编译器不支持加#,可能会报错,也可能会导致奇怪的输出;但是,大部分编译器都能正常输出,不至于当成一种错误。