进制
进制
基数
进位原则
基本符号
二进制(Bin)
2
逢2进1
0,1
八进制(Oct)
8
逢8进1
0~7
十进制(Dec)
10
逢10进1
0~9
十六进制(Hex)
16
逢16进1
0~9,A~F
如果某一个进制采用R R R 个基本符号,则称R R R 为基数 (二进制基数为2,十进制基数为10)。进制中每一位单位值称为位权 ,在整数部分第i i i 位的位权为R i R^{i} R i (个位为第0位),小数点右边第j j j 位的位权为R − j R^{-j} R − j
如12.3 4 10 = 1 × 1 0 1 + 2 × 1 0 0 + 3 × 1 0 − 1 + 4 × 1 0 − 2 12.34_{10}=1\times 10^1+2\times 10^0 + 3\times 10^{-1}+4\times10^{-2} 12.3 4 10 = 1 × 1 0 1 + 2 × 1 0 0 + 3 × 1 0 − 1 + 4 × 1 0 − 2
在二进制里,二进制的一位为1bit(1b),连续的8bits为一字节(byte)(1B)
R进制下的每一位数的范围为:0 ≤ a i ≤ R − 1 0 \le a_i \le R-1 0 ≤ a i ≤ R − 1
不同进制的转化
二进制转十进制
直接用每一位上的数乘以其对应的位权,再对每一位的结果求和即可得到十进制值。
如:101 1 2 = 1 × 2 3 + 0 × 2 2 + 1 × 2 1 + 1 × 2 0 = 1 1 10 1011_{2} = 1\times 2^3+0\times 2^2+1\times 2^1+1\times 2^0=11_{10} 101 1 2 = 1 × 2 3 + 0 × 2 2 + 1 × 2 1 + 1 × 2 0 = 1 1 10
R进制转十进制
A R = a n a n − 1 . . . a 2 a 1 a 0 A_R=a_na_{n-1}...a_2a_1a_0
A R = a n a n − 1 ... a 2 a 1 a 0
A 10 = a n × R n + a n − 1 × R n − 1 + . . . + a 2 × R 2 + a 1 × R 1 + a 0 × R 0 A_{10}=a_n\times R^{n} +a_{n-1}\times R^{n-1}+...+a_2\times R^2+a_1\times R^1 + a_0\times R^0
A 10 = a n × R n + a n − 1 × R n − 1 + ... + a 2 × R 2 + a 1 × R 1 + a 0 × R 0
十进制转R进制
整数部分–除R取余法:
已知十进制数x x x ,求R R R 进制下的数值
将x x x 除以R R R ,记录所得余数r r r
用得到的商作为新的被除数x x x ,重复上述过程,直到x x x 为0
最后倒序输出每次除法得到的余数
小数部分–乘R R R 取整法:
已知十进制小数的小数部分为x x x
将x x x 乘以R R R ,取乘积的整数部分作为小数部分的第一位
然后取乘积的小数部分作为新的x x x
重复上述过程,直到乘积为0,或已取得对应精度的小数位数。
二,八,十六进制的快速转化
由于2 3 = 8 , 2 4 = 16 2^3=8 ,\space 2^4=16 2 3 = 8 , 2 4 = 16 ,所以一位八进制可以用三位二进制数 表示,一位十六进制数可以用四位二进制数 表示,即三位一并法 ,四位一并法
注意在转化时,二进制高位不足时要补足0
如:110001 0 2 = 0110 001 0 2 = 6 2 16 1100010_2=0110\space 0010_2=62_{16} 110001 0 2 = 0110 001 0 2 = 6 2 16
Dec
0
1
2
3
4
5
6
7
Bin
0000
0001
0010
0011
0100
0101
0110
0111
Oct
0
1
2
3
4
5
6
7
Hex
0
1
2
3
4
5
6
7
Dec
8
9
10
11
12
13
14
15
Bin
1000
1001
1010
1011
1100
1101
1110
1111
Oct
10
11
12
13
14
15
16
17
Hex
8
9
A
B
C
D
E
F
计算机中的运算
CPU一次只能处理有限位数的二进制数 ,如32为的CPU一次最多处理32位的二进制数。计算机的整数有两种类型:无符号整数,有符号整数
二进制下的加减法与十进制类似,但由于CPU一次处理的位数有限所以可能会出现两个整数相加的和超出最大能够表示的位数,此时最高位就会丢失,称为溢出 .
无符号整数的乘法与除法:
乘法:由基本的二进制加法和移位来完成。(类似于小学的竖式运算)
除法:由二进制减法和移位来完成。(类似于小学的竖式运算)
有符号整数的表示法
(以8位CPU为例)
8位CPU的表示范围为[ 0 , 255 ] [0,255] [ 0 , 255 ] ,将区间分为两部分[ 0 , 127 ] , [ 128 , 255 ] [0,127],[128,255] [ 0 , 127 ] , [ 128 , 255 ]
注意到区间[ 0 , 127 ] [0,127] [ 0 , 127 ] 的每个数的二进制数首位均为0;而[ 128 , 255 ] [128,255] [ 128 , 255 ] 区间的每个数的二进制数首位均为1
计算机中,前一个区间表示正数范围[ 0 , 127 ] [0,127] [ 0 , 127 ] ,后一个区间表示负数范围[ − 128 , − 1 ] [-128,-1] [ − 128 , − 1 ] 。
这样表示是合理的,如:(CPU自动舍去超过最大位数的部分)0000000 1 2 ( 1 ) + 1111111 1 2 ( − 1 ) = 10000000 0 2 = 0000000 0 2 ( 0 ) 00000001_2(1)+11111111_2(-1)=100000000_2=00000000_2(0) 0000000 1 2 ( 1 ) + 1111111 1 2 ( − 1 ) = 10000000 0 2 = 0000000 0 2 ( 0 )
对于一个正整数x x x ,它的相反数− x -x − x 所对应的无符号整数为2 8 − x 2^8-x 2 8 − x
负数的补码表示法
一个二进制数x
的补码为~x+1
,即对它的每一位取反后再加上1.
正负数之间的转化可以简单地通过取补码来实现
如:1111100 1 2 ( − 7 ) = > 00000110 + 1 = 0000011 1 2 ( 7 ) 11111001_2(-7)=>00000110+1=00000111_2(7) 1111100 1 2 ( − 7 ) => 00000110 + 1 = 0000011 1 2 ( 7 )
用n位补码表示有符号整数的范围为[ − 2 n − 1 , 2 n − 1 − 1 ] [-2^{n-1},2^{n-1}-1] [ − 2 n − 1 , 2 n − 1 − 1 ]
一个带符号整数的二进制值被称为真值,如− 7 -7 − 7 的真值为1111100 1 2 11111001_2 1111100 1 2
带符号整数的运算溢出
加减法的溢出:
两个正数相加:可能发生正溢出(相加结果的最高位数为1)
如:120 + 30 = 0111100 0 2 + 0001111 0 2 = 1001011 0 2 ( − 106 ) 120+30=01111000_2+00011110_2=10010110_2(-106) 120 + 30 = 0111100 0 2 + 0001111 0 2 = 1001011 0 2 ( − 106 )
一正一负相加:不可能发生溢出
两个负数相加:可能发生负溢出(相加结果的最高位数为0)
如:− 120 + ( − 30 ) = 1000100 0 2 + 1110001 0 2 = 10110101 0 2 = 0110101 0 2 ( 106 ) -120+(-30)=10001000_2+11100010_2=101101010_2=01101010_2(106) − 120 + ( − 30 ) = 1000100 0 2 + 1110001 0 2 = 10110101 0 2 = 0110101 0 2 ( 106 )
浮点数
计算机中带小数点的数称为浮点数
工业上使用IEEE754 二进制浮点数算术标准来表示浮点数。
一个IEEE754 浮点数最多用64位 来存放,所以有一定的精度损失
IEEE754标准
在IEEE754下,一个浮点数由三个部分组成:符号位s s s ,指数e e e ,尾数m m m 。
即a = ± m × 2 e a=\pm m\times 2^e a = ± m × 2 e 的形式
符号位:表示正数时为0,表示负数时为1
尾数:用来存储形如1. d 1 d 2 . . . d i . . . 1.d_1d_2...d_i... 1. d 1 d 2 ... d i ... 的二进制小数的小数部分,其中d i ∈ { 0 , 1 } d_i \in \{0,1\} d i ∈ { 0 , 1 } 。尾数只存储小数点后面的部分,不需要存储最前面的1。最多储存的d i d_i d i 个数决定了表示的精度,不足的位数补足0.
指数:指数部分的正负不采用补码表示 法,而是通过中间数的偏移 来表示正负。(便于计算指数之间的差距和比较两个指数的大小)
如8位指数部分下:中间数为01111111 ( 127 ) 01111111(127) 01111111 ( 127 ) ,所以用真值127 ( 01111111 ) 127(01111111) 127 ( 01111111 ) 来表示指数部分的0;01111110 ( 126 ) 01111110(126) 01111110 ( 126 ) ,表示指数部分的-1;10000000 ( 128 ) 10000000(128) 10000000 ( 128 ) 表示指数部分的1,以此类推即可。
IEEE754-32 位浮点数的组成:1位的s+8位的e+23位的m
IEEE754-64 位浮点数的组成:1位的s+11位的e+52位的m
IEEE浮点数的加减法:先移动较小数的小数点,使得两个浮点数的指数部分相同再进行加法运算。
十进制小数向IEEE浮点数的转化
写出十进制小数对应的二进制小数。(5.62 5 10 = 101.10 1 2 5.625_{10}=101.101_2 5.62 5 10 = 101.10 1 2 )
对二进制小数的小数点移动进行标准化。(101.10 1 2 = 1.01101 × 2 2 101.101_2=1.01101\times 2^2 101.10 1 2 = 1.01101 × 2 2 )
标准化:将小数点移动到第一个出现的1之后,还要补上对应的2的n次幂
将标准化后的小数写成IEEE形式(01000000101101000000000000000000 01000000101101000000000000000000 01000000101101000000000000000000 )
如:5.625,即符号位0+指数10000001+尾数01101000…(23位)
程序实现练习
练习1:输入一个十进制数和n,输出对应的n位二进制补码
个人实现仅供参考:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 bits = int (input ("输入二进制位数n:" )) num = int (input ("输入十进制数:" )) def toBin (n ): if n==0 : return "0" if n==1 : return "1" return toBin(n//2 )+toBin(n%2 ) def format (n,bits=8 ): if len (n) < bits: d = bits - len (n) head = "0" *d return head+n elif len (n) > bits: return n[-bits:] return n def convert (n ): str = "" for i in range (len (n)): if (n[i]=='0' ): str += "1" elif (n[i]=='1' ): str += "0" return str def add1 (n ): l = [int (i) for i in n] l[len (n)-1 ] += 1 for i in range (len (n)-1 ,0 ,-1 ): if l[i] > 1 : l[i] %= 2 l[i-1 ] += 1 if l[0 ] == 2 : l[0 ] = 0 res = "" for i in l: res += str (i) return res c = toBin(abs (num)) c = format (c,bits) if num >= 0 : print (c) else : c = convert(c) c = add1(c) c = format (c,bits) print (c)
练习2:
输入一个十进制小数,输出对应的IEEE32位浮点数,并输出其对应的十进制小数
个人实现的是64位IEEE,仅供参考:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 MSIZE = 52 ESIZE = 11 from decimal import *getcontext().prec = 70 def toBinA (string ): i = int (string) if i < 2 : return str (i) return toBinA(str (int (string)//2 )) + str (int (string)%2 ) def toBinB (string ): result = "" i = Decimal(string) while (len (result)<=70 ): i*=2 result += str (int (i)) i-=int (i) return result def toBinC (int ): i = int + 1023 return toBinA(i) def toDecA (bin ): sum = 0 for i in range (len (bin )): sum = sum *2 + int (bin [i]) return sum def toDecB (bin ): sum = 0 if (int (bin )==0 ): return 0.0 for i in range (len (bin )): sum += int (bin [i]) * (Decimal("0.5" )**(i+1 )) return sum def toDecC (bin ): dec = toDecA(bin ) return dec-1023 def dec2float (string ): string = str (float (string)) a,b = string.split("." )[0 ],string.split("." )[1 ] b = "0." +b if Decimal(string)<0 : sign = '1' ;a=str (abs (int (a))) else : sign = '0' a = toBinA(a) b = toBinB(b) if "1" in a: e = len (a) - 1 b = a[1 :] + b a = "1" e = toBinC(e) else : head1 = b.find("1" ) if (head1 == -1 ): e = '0' *ESIZE mantissa = '0' *MSIZE return sign+e+mantissa e = -(head1+1 ) a = "1" b = b[(head1+1 ):] e = toBinC(e) mantissa = b[:MSIZE] if len (mantissa)<MSIZE: mantissa+='0' *(MSIZE-len (mantissa)) if len (e)<ESIZE: e='0' *(ESIZE-len (e)) + e return sign+e+mantissa def float2dec (string ): sign = string[0 ] e = string[1 :12 ] mantissa = string[12 :] e = toDecC(e) mantissa = toDecB(mantissa) mantissa = Decimal("1." +str (mantissa).split("." )[1 ]) num = mantissa * Decimal(2 ** e) if sign == "1" : return -num else : return num i = input () print (dec2float(i))print (float2dec(dec2float(i)))