数据结构与算法--⼆进制详解Python⼆进制算法详解史上最详细的⼆进制讲解彻
底搞懂原码、。。。
阅读⽬录
原码、反码、补码
机器数和真值
机器数:
⼀个数在计算机中的⼆进制表⽰形式, 叫做这个数的机器数。
机器数是带符号的,在计算机⽤⼀个数的最⾼位,称为符号位:⽤来存放符号, 正数为0, 负数为1.
例如:⼗进制中的数 +3 ,假设计算机字长为8位,转换成⼆进制就是:00000011 ;如果是 -3 ,就是 10000011;这⾥的00000011 和 10000011 就是机器数(其实就是原码 表⽰形式)
真值:
因为第⼀位是符号位,所以机器数的形式值就不等于真正的数值。例如上⾯的有符号数 10000011,其最⾼位1代表负,其真正数值是 -3 ⽽不是 “⽆符号数” 131(⼆进制数:10000011 转换成⼗进制等于131)。所以,为区别起见,将带符号位的机器数对应的真正数值称为机器数的真值(符号位直接按照0或者1 转换成 “+”“-” 占⼀个位置);例如:0000 0001的真值 = +000 0001 = +1;1000 0001的真值 = –000 0001 = –1;所以 ⼆进制表⽰形式:10000011 --> - 000 0011 它的真值为 -3 ;其⼆进制结果为 131,
原码、反码、补码的基础
对于⼀个数,计算机要使⽤⼀定的编码⽅式进⾏存储:原码,反码,补码是机器存储⼀个具体数字的编码⽅式;既然有三种存储形式,那么计算机会选取哪种通⽤形式呢?答案是:选取补码,即数值在计算机中是以补码的形式来存储的,下⾯分步来说明为什么要⽤补码来存?
原码:
(1)原码(true form)是⼀种计算机中对数字的⼆进制定点表⽰⽅法。原码表⽰法在数值前⾯增加了⼀位符号位(即最⾼位为符号位):正数该位为0,负数该位为1(0有两种表⽰:+0和-0),其余位表⽰数值的⼤⼩!⽐如如果是8位⼆进制 1 :
[+1]原=00000001
[-1]原=10000001
(2)因为第⼀位是符号位,所以8位⼆进制数的取值范围就是:
[11111111,01111111]
#换成数字为:
[-127,127]
(3)原码的优缺点:优点是简单直观,⼤脑最容易理解,例如,我们⽤8位⼆进制表⽰⼀个数,+11的原码为00001011,-11的原码就是10001011;缺点就是:原码不能直接参加运算,如果运算可能会出错。例如:数学上,1+(-1)=0,⽽在⼆进制中:
00000001+10000001=10000010# 换算成⼗进制为-2 ;这显然出错了
所以原码的符号位不能直接参与运算,必须和其他位分开,这就增加了硬件的开销和复杂性
反码:
正数的反码还是其本⾝;负数的反码是在其原码的基础上,符号位不变,其余各个位取反
[+1]=[00000001]原=[00000001]反
[-1]=[10000001]原=[11111110]反
'''
如果⼀个反码表⽰的是负数, 除了直观的看到它的最⾼位是1,它表⽰是个负数外,
我们⽆法直观的得出来它的具体数值,通常要将其转换成原码再计算
这⾥假如直接将负数的⼆进制反码,按:最⾼位为符号位“-” 剩下的位数按照⼆进制来转换的话
'''
11111110-->-126显然也不是原来的值-1
补码:
正数的补码还是其本⾝;负数的补码是其反码+1(也即是:在其原码的基础上, 符号位不变, 其余各位取反, 最后+1)
[+1]=[00000001]原=[00000001]反=[00000001]补
[-1]=[10000001]原=[11111110]反=[11111111]补
# 如果⼀个补码表⽰的是负数, 除了直观的看到它的最⾼位是1,它表⽰是个负数外,
# 我们⽆法直观的得出来它的具体数值,通常要将其转换成原码再计算
# 这⾥假如直接将负数的⼆进制补码,按:最⾼位为符号位“-” 剩下的位数按照⼆进制来转换的话
11111111-->-127显然也不是原来的值-1
计算机为什么选⽤以补码的形式来存储?
(1)计算机只有加法运算,没有设置减法运算!原因是:对于计算机,加减乘数已经是最基础的运算,要设计的尽量简单;计算机辨别"符号位"显然会让计算机的基础电路设计变得⼗分复杂!于是⼈们想出了将符号位也参与运算的⽅法;我们知道,根据运算法则:减去⼀个正数等于加上⼀个负数,即: 1-1 = 1 + (-1) = 0;所以机器可以只有加法⽽没有减法,这样计算机运算的设计就更简单!
那么计算机要选取哪种码值来做加法运算呢?我们先来⼀⼀尝试,并看结果为什么要选择补码!
# 先补充:计算机是如何计算减法的,看下⾯的例⼦(这⾥我们假设已经知道是选取补码来运算)
# 求:1-2 的值
# ⾸先:将1-2 变成 1+(-2),然后分别将1的补码和-2的补码拿来运算
<0001(正数1补码还是本⾝)
+
<1110(-2的补码)
=
<1111(补码)
# 上⾯结果⼀看,不对啊,怎么不是-1啊?不急,有规定,我们先看最⾼位符号位为1,说明它是个负数
# 如果得到的是负数的话,我们需要将它转换成原码的表⽰形式,如果是正数(最⾼位符号位为0)则不需要
# 所以我们接着做:
<1111(补码)
<1110(反码)
<0001(原码)
即结果为-1
(2)如果选择 原码来 存储:
# 假设要计算⼗进制的表达式: 1-1=0
1-1=1+(-1)=[00000001]原+[10000001]原=[10000010]原=-2
'''
数学二进制的算法所以如果⽤原码表⽰, 让符号位也参与计算, 显然对于减法来说, 结果是不正确的,
这也就是为何计算机内部不使⽤原码表⽰⼀个数
'''
(3)如果选择 ⽤ 反码 来存储:
# 同样假设要计算⼗进制的表达式: 1-1=0
1-1=1+(-1)=[00000001]原+[10000001]原=[00000001]反+[11111110]反=[11111111]反
# 再将反码转成原码:
[11111111]反=[10000000]原=-0
'''
看上去可⾏?因为按照我们通常认为的 0 和 -0 是⼀样;如果 0 和 -0 是⼀样的话,那么就有 [0000 0000]原和 [1000 0000]原两个编码表⽰0,这样显然会造成混乱,⽽且[1000 0000]也⽤来表⽰0造成浪费!
'''
(4)看了上⾯两种码值的缺点后,我们选取补码的话,就能很好的解决0的符号以及两个编码的问题:
# 同样假设要计算⼗进制的表达式: 1-1=0 这次我们选取补码
1-1=1+(-1)=[00000001]原+[10000001]原=[00000001]补+[11111111]补=[00000000]补
(这⾥或许会有⼀个疑问:[00000001]补+[11111111]补相加[00000000]补进位后,多出的⼀位去哪了?后⾯会详细说明下)
# 再将补码转成原码:
[00000000]补=[00000000]原# 可以把它看成⼀个正数,或者是单独的0
(补充:0的反码、补码 都是0)
接着上⾯,这样0⽤[0000 0000]表⽰,⽽且可以⽤[1000 0000]表⽰ -128,也即是⽤ -128 代替了 -0 (看下⾯详细推导):
(-1)+(-127)=[10000001]原+[11111111]原=[11111111]补+[10000001]补=[10000000]补
即是,相⽐较于反码和原码要将最⾼位当做⼀个符号位的做法,采取补码可以多表⽰⼀位(之前我们认为1111 1111是 -127 它最⼩也只能表⽰这么多了,因为最⾼位是符号位,剩余位数都为1了)!
同时:使⽤补码,不仅仅修复了0的符号以及存在两个编码的问题,⽽且还能够多表⽰⼀个最低数;这就是为什么8位⼆进制, 使⽤原码或反码表⽰的范围为[-127, +127];⽽使⽤补码表⽰的范围为[-128, 127];
因为机器使⽤补码, 所以对于编程中常⽤到的32位int类型, 可以表⽰范围是: [-231, 231-1] 因为第⼀位表⽰的是符号位,⽽使⽤补码表⽰时⼜可以多保存⼀个最⼩值.
Python中负数的处理
先补充 Python中求⼆进制数的内置函数:bin() 以及int() 的⽤法
(1)内置函数 bin() 返回⼀个整数 int 或者长整数 long int 的⼆进制表⽰。
>>>bin(10)
'0b1010'
>>>bin(20)
'0b10100'
(2)内置函数 int() ⽤于将⼀个字符串或数字转换为整型。
语法格式:class int(x, base=10) ;x – 字符串或数字,base – 进制数,默认⼗进制。
>>>int()# 不传⼊参数时,得到结果0
>>>int(3)
3
>>>int(3.6)
3
>>>int('12',16)# 如果是带参数base的话,12要以字符串的形式进⾏输⼊,12 为 16进制
18
>>>int('0xa',16)
10
>>>int('10',8)
8
注意事项:
若 x–为纯数字时,不能设置base进制,否则报错
>>>int(3.1415926)
3
>>>int(-11.123)
-11
>>>int(2.5,10)
#报错
>>>int(2.5)
2
若 x 为 str,则 base 可略可有。
base 存在时,视 x 为 base 类型数字,并将其转换为 10 进制数字。
若 x 不符合 base 规则,则报错。
>>>int("9",2)#报错,因为2进制⽆9
>>>int("9")
9#默认10进制
>>>int("3.14",8)
>>>int("1.2")
#均报错,str须为整数
>>>int("1001",2)
9
# "1001"才是2进制格式,并转化为⼗进制数字9
>>>int("0xa",16)
10
# ≥16进制才会允许⼊参为a,
>>>int("b",8)#报错
>>>int("123",8)
83
# 视123为8进制数字,对应的10进制为83
Python 对于负数的存储⽅式和 c++/c/java 有点不⼀样,和上⾯我们说的理论有点不⼀样!
先看下⾯的例⼦:
a1 =bin(-3)
print(a2)
a2 =bin(3)
print(a2)
b =bin(-3&0xffffffff)
print(b)
c =bin(0xfffffffd)
print(c)
'''输出结果分别为:
-0b11 # 特别注意这个
0b11
0b11111111111111111111111111111101
0b11111111111111111111111111111101
'''
(1)注意,此时Python的坑出现了,⾸先我们知道Python 中的整型也是⽤ 补码形式存储的,Python 中 bin ⼀个正数(⼗进制表⽰)结果就是它的⼆进制补码表⽰形式(看上⾯的a2结果);但是Python 中 bin ⼀个负数(⼗进制表⽰),输出的是它的原码的⼆进制表⽰加上个负号,这显然不是我们要求的负数的补码,因为我们可以求出 -3的补码应该为:ob11111111111111111111111111111101,⽽不应该是-0b11 ,下⾯将详细将Python中遇到负数要如何处理!
(2)Python 中 bin ⼀个负数(⼗六进制表⽰),输出的是对应的⼆进制表⽰,把上⾯的例⼦拿下来↓↓↓
c =bin(0xfffffffd)# 看上图
print(c)
# 结果为:0b11111111111111111111111111111101 (补码)
#(上图中:4294967293不是真值,它是不考虑最⾼符号位,直接得出的⼆进制值,这个可以不看)
# 将上⾯结果的补码,转换成原码为:0b10000000000000000000000000000011 刚好就是-3
# 再来⽤下内置⽅法 int()
c =bin(0xfffffffd)
print(c)
# c='0b11111111111111111111111111111101' (补码)
c1 ='0b10000000000000000000000000000011'(c的原码)
print(int(c, base=2))# 将c的原码直接⽤int算,⼜会出现⼀个坑,Python的int()也不认符号位,如下结果
'''输出结果
0b11111111111111111111111111111101
2147483651 # 这不是真值
'''
# 所以我们也不要⽤ int()直接去将负数的⼆进制数还原成10进制,下⾯会将快捷⽅法
那问题⼜来了,怎么得到⼀个负数的⼗六进制数表⽰⽅式?
答案是: 负数 & 0xFFFF FFFF 就可以得到这个负数的⼗六进制表⽰
我们来推理⼀下:
11111111111111111111111111111101(-3的补码)
&
11111111111111111111111111111111(0XFFFFFFFF)
=
11111111111111111111111111111101(⼆进制补码)
# 刚好可以换成⼗六进制的
0xFFFFFFFD
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论