进制

进制 基数 进位原则 基本符号
二进制(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
  • 如果某一个进制采用RR个基本符号,则称RR基数(二进制基数为2,十进制基数为10)。进制中每一位单位值称为位权,在整数部分第ii位的位权为RiR^{i}(个位为第0位),小数点右边第jj位的位权为RjR^{-j}
  • 12.3410=1×101+2×100+3×101+4×10212.34_{10}=1\times 10^1+2\times 10^0 + 3\times 10^{-1}+4\times10^{-2}
  • 在二进制里,二进制的一位为1bit(1b),连续的8bits为一字节(byte)(1B)
    • 即,1B=8b1B=8b
  • R进制下的每一位数的范围为:0aiR10 \le a_i \le R-1

不同进制的转化

二进制转十进制

  • 直接用每一位上的数乘以其对应的位权,再对每一位的结果求和即可得到十进制值。
  • 如:10112=1×23+0×22+1×21+1×20=11101011_{2} = 1\times 2^3+0\times 2^2+1\times 2^1+1\times 2^0=11_{10}

R进制转十进制

  • 由以上公式可以推出更一般的转化十进制的方法

AR=anan1...a2a1a0A_R=a_na_{n-1}...a_2a_1a_0

A10=an×Rn+an1×Rn1+...+a2×R2+a1×R1+a0×R0A_{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

十进制转R进制

  • 整数部分–除R取余法:
    • 已知十进制数xx,求RR进制下的数值
    • xx除以RR,记录所得余数rr
    • 用得到的商作为新的被除数xx,重复上述过程,直到xx为0
    • 最后倒序输出每次除法得到的余数
  • 小数部分–乘RR取整法:
    • 已知十进制小数的小数部分为xx
    • xx乘以RR,取乘积的整数部分作为小数部分的第一位
    • 然后取乘积的小数部分作为新的xx
    • 重复上述过程,直到乘积为0,或已取得对应精度的小数位数。

二,八,十六进制的快速转化

  • 由于23=8, 24=162^3=8 ,\space 2^4=16,所以一位八进制可以用三位二进制数表示,一位十六进制数可以用四位二进制数表示,即三位一并法四位一并法
  • 注意在转化时,二进制高位不足时要补足0
  • 如:11000102=0110 00102=62161100010_2=0110\space 0010_2=62_{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,127],[128,255][0,127],[128,255]
  • 注意到区间[0,127][0,127]的每个数的二进制数首位均为0;而[128,255][128,255]区间的每个数的二进制数首位均为1
  • 计算机中,前一个区间表示正数范围[0,127][0,127],后一个区间表示负数范围[128,1][-128,-1]
  • 这样表示是合理的,如:(CPU自动舍去超过最大位数的部分)000000012(1)+111111112(1)=1000000002=000000002(0)00000001_2(1)+11111111_2(-1)=100000000_2=00000000_2(0)
  • 对于一个正整数xx,它的相反数x-x所对应的无符号整数为28x2^8-x

负数的补码表示法

  • 一个二进制数x的补码为~x+1,即对它的每一位取反后再加上1.
  • 正负数之间的转化可以简单地通过取补码来实现
    • 如:111110012(7)=>00000110+1=000001112(7)11111001_2(-7)=>00000110+1=00000111_2(7)
  • 用n位补码表示有符号整数的范围为[2n1,2n11][-2^{n-1},2^{n-1}-1]
  • 一个带符号整数的二进制值被称为真值,如7-7的真值为11111001211111001_2

带符号整数的运算溢出

  • 加减法的溢出:
  • 两个正数相加:可能发生正溢出(相加结果的最高位数为1)
  • 如:120+30=011110002+000111102=100101102(106)120+30=01111000_2+00011110_2=10010110_2(-106)
  • 一正一负相加:不可能发生溢出
  • 两个负数相加:可能发生负溢出(相加结果的最高位数为0)
  • 如:120+(30)=100010002+111000102=1011010102=011010102(106)-120+(-30)=10001000_2+11100010_2=101101010_2=01101010_2(106)

浮点数

  • 计算机中带小数点的数称为浮点数
  • 工业上使用IEEE754二进制浮点数算术标准来表示浮点数。
  • 一个IEEE754浮点数最多用64位来存放,所以有一定的精度损失

IEEE754标准

  • 在IEEE754下,一个浮点数由三个部分组成:符号位ss,指数ee,尾数mm
  • a=±m×2ea=\pm m\times 2^e 的形式
  • 符号位:表示正数时为0,表示负数时为1
  • 尾数:用来存储形如1.d1d2...di...1.d_1d_2...d_i...的二进制小数的小数部分,其中di{0,1}d_i \in \{0,1\}。尾数只存储小数点后面的部分,不需要存储最前面的1。最多储存的did_i个数决定了表示的精度,不足的位数补足0.
  • 指数:指数部分的正负不采用补码表示法,而是通过中间数的偏移来表示正负。(便于计算指数之间的差距和比较两个指数的大小)
  • 如8位指数部分下:中间数为01111111(127)01111111(127),所以用真值127(01111111)127(01111111)来表示指数部分的0;01111110(126)01111110(126),表示指数部分的-1;10000000(128)10000000(128)表示指数部分的1,以此类推即可。
  • IEEE754-32位浮点数的组成:1位的s+8位的e+23位的m
  • IEEE754-64位浮点数的组成:1位的s+11位的e+52位的m
  • IEEE浮点数的加减法:先移动较小数的小数点,使得两个浮点数的指数部分相同再进行加法运算。

十进制小数向IEEE浮点数的转化

  • 写出十进制小数对应的二进制小数。(5.62510=101.10125.625_{10}=101.101_2)
  • 对二进制小数的小数点移动进行标准化。(101.1012=1.01101×22101.101_2=1.01101\times 2^2)
  • 标准化:将小数点移动到第一个出现的1之后,还要补上对应的2的n次幂
  • 将标准化后的小数写成IEEE形式(0100000010110100000000000000000001000000101101000000000000000000)
  • 如: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)

# 格式化(将其格式化为n位二进制)
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)

#输入样例:8 -7
#输出样例:11111001
  • 练习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)))



#测试样例

#>>> 3.14
#0100000000001001000111101011100001010001111010111000010100011110
#3.1399999999999996802557689079549163579940795898437500

#>>> 0
#0000000000000000000000000000000000000000000000000000000000000000
#1.112536929253600691545116358666202032109607990231165915276663708443602E-308

#>>> -5.625
#1100000000010110100000000000000000000000000000000000000000000000
#-5.6250000000000000000000000000000000000000000000000000