python入门(五)—— 整数,小数(浮点数),复数

整数(int)存储方式

关于数字类型的基本介绍,第三节已经说了大部分,这里不再赘述。下面要说一点关于数字存储的底层知识,这对于学过c/c++的同学很熟悉,如果不感兴趣,可以跳过。

我们知道,计算机上所有的东西都是二进制,也就是一串010001101如此等等的数字。对于一个两位的二进制,只能有四种排列:00,01,10,11,也就是说,只能用来表示四个不同的的东西,如果用来表示数字,只能表示0,1,2,3,当然如果你标新立异,非要用来表示5,6,7,8那也没关系,总之,最多表示四个不同的东西。对于n位二进制来说,可以表示
2 n 2^n 2n
个不同的东西。计算机上,这个n是有限的,显然,计算机上可以表示东西也是有限的。然而我们的数字是有无限多的,这就使得计算机不可能表示全部的整数,那么计算机有什么意义?好在,我们日常能够使用的数字,相对来说都不会很大。比如,如果你要为餐馆写一个统计天客人用餐信息的程序,一般也就100元左右一个人,一天的收入,肯定也不会超过一个亿。所以,我们只要能够表示我们常用的数字就行了。

现在的计算机一般都是64位,即CPU一次计算能够处理64位二进制,也叫64bit(比特)。如果用64bit来表示整数,那么可以表示整数个数为
2 64 = 18446744073709551616 2^{64} = 18446744073709551616 264=18446744073709551616
同时,为了能够表示负数,这么多数字中有一半是复数,还有一个是0,所以整数最大为(263-1),最小为-263

下面采用8bit整数来讲述一下整数在计算机实际上是怎样的。8bit一共256个数,其中127个正数加上0很多时候用来表示128个字符,称之为ASCII字符表,这对于c/c++同学很清楚,不知道的可以去百度一下。所以一般来说,把8bit叫做一个字节(Byte)。

正数就是一般的二进制排列

00000000   ---   0
00000001   ---   1
00000010   ---   2
00000011   ---   3
00000100   ---   4

01111111   ---   127

可以看到正数包括0,其二进制第一位是0,后面128个复数,二进制第一位就一直是1。复数是这样实现的:

例如-126,写出正126的二进制

01111110   ---   +126

将上面的二进制数字全部反过来,这一步叫求反码

10000001   ---   +126的反码

然后在这个反码上加1,叫做+126补码

10000010   ---   -126

就是-126的二进制表示

习题:试着写出-1,-128的二进制表示

将负数做这样的编码的好处是,负数和正数的加法,可以直接使用二进制加法计算,以127+(-126)为例

   01111111   ---   127
+  10000010   ---   -126
= 100000001   ---   ???
=  00000001   ---   1

不把第一位看作表示符号,而是普通的二进制数,直接相加,正常进位。但是相加结果超出了8bit,前面的位就直接舍去,这样直接就得到了1。这样,含有正负数的加减法的时候,计算机不用判断一个数是不是负数,直接按照正数来做加减法就是,这显然对于计算机运算速度是有好处的。

作业:按照二进制加法,127+1的二进制结果是什么,它对应哪一个数字的二进制表示,-128±3呢?

可以看见,二进制的整数表示是有限的,如果表示整数的bit数是固定的,那么会出现大数字不够表示的情况,这叫做溢出。上面的这个作业,其实说明了溢出之后的结果,是错误的,但是结果还会在表示范围之内。

python的整数存储

对于python,他自己实现了变长的整数。所谓变长,就是当数字不大时,可能(具体时这样的,还要看源代码)使用32位的整数,当数字比较大,位数不够用的时候,就加一段,还不够,再加。所以理论上,python的整数可以是任意大的。不过当数字很大的时候,运算速度也会变得很慢。

位运算

数字的位运算,是从二进制的角度,来操作整数,这在python中并不常用,但是有的时候,也会有意想不到的用处:

  • 位与&:将数字写成二进制表示,对每一位,做与运算:只有两个都是1时,结果为1,否者为0。
  00010111   ---   23
& 00100010   ---   34
= 00000010   ---   2

所以,在交互式编程处,输入23 & 34,得到:

>>> 23 & 34
2
  • 位或|:每一位,只要有一个是1,结果就是1,否者为0。
>>> 23 | 34
55
  • 位非~:每一位,数字反过来,其实就是求反码
>>> ~1
-2
  • 位异或^:每一位,两个数字不同,就是1,否则为0
>>> 23 ^ 34
53

后面几个的二进制过程就不写出来了,可以自己分析一下。异或有一个很有意思的用法,就是写整数交换算法:

传统的算法:

a = 100
b = 200
temp = a
a = b
b = temp

必须引入一个中间变量temp记录a的值,否者无法交换,使用异或则不用

a = a^b
b = a^b
a = a^b

经过上述三行代码,就可以完成,其中用到异或的性质a^a=0, a^b=b^a,可以自己推导一下。

浮点数

和整数一样,实数的显然也是二进制表示的,那么计算机能够表示的实数也是有限的。计算机表示实数的方式叫做浮点数,它使用一个bit表示数字的符号s,使用t个bit表示二进制的小数点后t位,用剩下的bit表示指数e,最后表示的浮点数的结果是
x = ( − 1 ) s ⋅ ( 0. a 1 a 2 ⋯ a t ) ⋅ β e x = (-1)^s\cdot (0.a_1a_2\cdots a_t)\cdot\beta^e x=(1)s(0.a1a2at)βe
二进制表示中,β=2,上面写作小数的形式 ( 0. a 1 a 2 ⋯ a t ) (0.a_1a_2\cdots a_t) (0.a1a2at),是二进制小数。

显然,对于像圆周率pi,自然对数常数e这些无理数,实际上是不能够使用浮点数表示。不过,从另外的角度看,我们实际工作中,对于实数的使用也并不是要求全部表示。比如,我们要计算某一个工程需要的水泥的量,我想应该还不至于精确到千克,大不了稍微多买一点,这个计算使用浮点数实际上已经足够精确。

32bit的浮点数,常常称为单精度数,一般能够精确到10-7,64bit的浮点数又叫双精度数,一般能够精确到10-16,这对于大多数情况下已经足够精确了。

在现在通用的64bit计算机上,python是使用64bit浮点数的,也就是双精度数,其最大的范围是:10-308到10308,当然正负数都支持。这使得在python中,有的时候算出巨大的整数,还真的不能转化为浮点数。

类型转换,可以这样来做

>>> int(12.3)
12
>>> float(10**1000)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OverflowError: int too large to convert to float

交互式下,type(12),就会输出int,是整数的类型名,浮点数的类型名是float。类型转换直接使用类型名作为函数就好了。上述结果中转化int(12.3)是转化成功的,而后面的转化失败,就是因为10**100太大了,超出了浮点数的表示范围。

python并没有拓展浮点数的使得其像整数一样可以无限多,因为这是不必要的。首先浮点数的拓展并不像整数那样容易,并且浮点数常用在高性能计算中,运算速度更重要,而不是精度。对于精度问题,需要数学家和计算机学家很仔细的控制,在开启一个超级计算程序之前,往往需要通过原理上的估算确保精度是足够的,这需要更加深入的理论。不过日常生活,普通的工作,双精度数已经没有任何问题了。

数学函数库

实数运算当然需要一些数学函数,下面是交互式命令行的一些演示

>>> import math        # 必须要先导入数学模块,否则后面的东西都无法使用
>>> math.asin(0.5)     # 反正弦
0.5235987755982989
>>> math.log(2.3)      # 对数函数
0.8329091229351039
>>> math.exp(2.3)      # 指数函数
9.974182454814718
>>> dir(math)          # 查看math模块中可用的东西
['__doc__', '__loader__', '__name__', '__package__', '__spec__', 'acos', 'acosh', 'asin', 'asinh', 'atan', 'atan2', 'atanh', 'ceil', 'copysign', 'cos', 'cosh', 'degrees', 'e', 'erf', 'erfc', 'exp', 'expm1', 'fabs', 'factorial', 'floor', 'fmod', 'frexp', 'fsum', 'gamma', 'gcd', 'hypot', 'inf', 'isclose', 'isfinite', 'isinf', 'isnan', 'ldexp', 'lgamma', 'log', 'log10', 'log1p', 'log2', 'modf', 'nan', 'pi', 'pow', 'radians', 'remainder', 'sin', 'sinh', 'sqrt', 'tan', 'tanh', 'tau', 'trunc']

其中下划线的这些可以先不管,对于后面的一些函数,如果不知道是什么意思,还记得前面说过的神器吗。交互式下使用help函数就是了,比如

>>> help(math.hypot)
Help on built-in function hypot in module math:

hypot(x, y, /)
    Return the Euclidean distance, sqrt(x*x + y*y).

其中还值得一提的是math模块里面还有几个常数

>>> math.pi
3.141592653589793
>>> math.e
2.718281828459045
>>> math.tau
6.283185307179586

关于实数,还有一个生成随机数的模块random

>>> import random
>>> random.random()
0.900955873808953
>>> random.random()
0.7794294909793891

你也可以用dir(random)看看其中有那些函数,随机数可以写好几本专著了,这里就不展开讲。

复数

复数可以使用下面两中方式创建

>>> 1+2j
(1+2j)
>>> complex(1,2)
(1+2j)
>>> complex(1)
(1+0j)

处理复数的模块是cmath

>>> import cmath
>>> cmath.sqrt(1+2j)
(1.272019649514069+0.7861513777574233j)

其他的和运算都差不多,不一一列举。关于复数的二进制表示,显然实部和虚部分开,分别使用整数或者浮点数的表示法就好了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值