Python中的数值类型

作者: 淡是养心药 | 来源:发表于2017-12-23 15:27 被阅读16次

    Python中的基本数据类型有数值类型、字符串型、列表、元组、字典、集合等。本章介绍数值类型。数值类型包括整型、布尔型、浮点型和复数类型。

    3.1 整型

    3.1.1 取值范围

    和其他语言一样,Python中也有整型数据类型,与很多语言不同的是,Python的最新版本中( Python 3.6.1)的整型可表示的数值范围非常大,其大小只受计算机内存大小的限制。例如,下面这个非常大的整数的整型运算,假如在C语言中,需要自行构造数据结构实现,而在Python 3.6.1中,使用语言内置的整型即可实现,而且其算法经过了优化,计算速度相当快:

    >>>> 1111**1000  # 1111的1000次方
    >>>> 51767708443443359586930107440187874650172620553914244948309038572762729578198094152582315911797136186237712453.....
    

    3.1.2 支持的运算

    我们前面讲过,当我们谈论某个数据类型时,一方面规定了该数据类型的取值及其范围,另一方面规定了在该数据类型上可以执行的运算。对于整型,可以进行算术运算、位运算和比较运算。

    1. 算术运算

    整型和浮点型都支持下面几种算术运算:

    运算 结果 注释
    x+y x和y的和(sum of x and y)
    x-y x和y的差(difference of x and y)
    x*y x和y的积(product of x and y)
    x/y x和y的商(quotient of x and y) (4)
    x//y x和y的地板商(floor quotient of x and y) (1)
    x%y x/y的余数( remainder of x/y)
    -x 对x取负数(x negated)
    +x x本身(x unchanged)
    abs(x) x的绝对值或称其大小(absolute value or magnitude of x)
    int(x) 将x转换为整型 (3)
    divmod(x,y) 由(x//y, x%y)组成的数值对
    pow(x,y) x的y次方 (2)
    x**y x的y次方 (2)

    (1) 也称为整数除法,对运算结果进行了取整,不过结果的类型不一定是整型(int),结果总是向负无穷方向取整: 1//2=0, (-1)//2=-1, 1//(-2)=-1,而(-1)//(-2)=0。

    (2) 和其他编程语言一样,Python定义pow(0,0)或0**0为1。

    (3) int()是一个类型转换函数,把其他类型的值转换成整型值。从浮点型转换为整型将像C语言中那样对浮点数进行舍入(舍去浮点数中的任何小数,而不是四舍五入)

    (4) 也称为小数除法,结果是一个"精确的"小数

    >>> i=5//3  # i=1
    >>> i=5%3   # i=2
    >>> i=abs(-1)  # i=1
    >>> i=int("1234") # i=1234
    >>> pair=divmod(5,3)  # pair=(1,2)
    >>> i=power(2,3)  # i=8
    >>> i=2**3.       # i=8
    

    2. 位运算

    整型数支持位运算,就是对整型数对应的二进制数进行按位操作。有以下几种位运算:

    操作(运算) 结果 注释
    x|y x和y的按位或
    x^y x和y的按位异或
    x&y x和y的按位与
    x<<n x左移n位 (1)(2)
    x>>n x右移n位 (1)(3)
    ~x x按位取反

    (1) n为负数时不合法,将会出错

    (2) 左移n位等效于乘以pow(2, n)的积,不进行溢出检查

    (3) 右移n位等效于除以pow(2,n)的商,不进行溢出检查

    # 按位或运算是对整数的对应位进行或运算,得到的结果,
    # 位或运算遵循下面的表,参与计算的两位中只要有1个是1,结果就是1
    # 1st digit    1  1  0  0
    # 2nd digit    1  0  1  0
    # result digit 1  1  1  0
    >>> i=10  # i的二进制表示(4位即可,高位为0) 1010 
    >>> j=8   # j的二进制表示(4位即可,高位为0) 1000
    >>> k=i|j # k的二进制表示(4位即可,高位为0) 1010,其十进制是10
    
    # 按位与运算是对整数的对应位进行与运算,得到的结果
    # 位与运算遵循下面的表,参与计算的两位中都是1,结果才是1
    # 1st digit    1 1 0 0
    # 2nd digit    1 0 1 0
    # result digit 1 0 0 0
    >>> i=10  # i的二进制表示(4位即可,高位为0) 1010 
    >>> j=8   # j的二进制表示(4位即可,高位为0) 1000
    >>> k=i&j # k的二进制表示(4位即可,高位为0) 1000,其十进制是8
    
    # 左移位运算就是对将整型数的二进制表示按位左移,
    # 右侧补0,相当于乘以2**n
    >>> i=10  # i的二进制表示(16位即可,高位为0) 0000 0000 0000 1010
    >>> j=i<<2  #j的二进制表示.                0000 0000 0010 1000  
    >>> j
    40
    
    # 右移位运算与左移位相反
    >>> i = 10 # 0000 0000 0000 1010
    >>> j=i>>2 # 0000 0000 0000 0010 , 十进制为2
               # 可以看到起整数除法的效果,即j=i//4
    

    上面的代码中介绍了正整数的右移位运算,负整数的二进制表示和右移位运算该怎么做呢?这涉及到数字表示的原码、反码和补码。先透露一点,计算机中常用补码表示,以解决正负数混合运算的问题。

    为了简化问题,我们用四位二进制位表示一个整数。

    原码: 一个整数的最基本的二进制表示,在最高位用1表示负号,最高位的0不表示正号,仍表示0。如: 十进制数3的原码是0011, -3的原码是 1011

    我们看到,使用原码,对于正整数的相加,能够很好地解决了,但正负数相加就不对了。比如:

    3   0 0 1 1       3  0 0 1 1
    +3   0 0 1 1    +(-3) 1 0 1 1
    ------------    -------------
    6   0 1 1 0      -6  1 1 1 0
    

    因此,引入了反码

    反码: 正数的反码就是其原码,负数的反码符号位不变,其余各位按位取反。十进制数3的反码是: 0011, -3的反码是1100。0的反码有两个(±0),分别是0000和1111

     3    0 0 1 1       3    1 1 0 0
    +3    0 0 1 1    +(-3)   1 1 0 0
    -------------    ---------------
     6    0 1 1 0        0   1 1 1 1  0的反码
    

    反码中0存在±0两种表示,这不美观,因此,我们引入了补码,解决了上述问题:

    补码: 正数的补码是其原码,负数的补码是反码加1,十进制数3的补码是0011,十进制数-3的补码是1101。这时,+0和-0的补码就相同了。

     3    0 0 1 1       3    0 0 1 1
    +3    0 0 1 1    +(-3)   1 1 0 1
    -------------    ---------------
     6    0 1 1 0       0 (1)0 0 0 0  (最高位的1由于超出4位而被丢弃)
    

    负整数的右移位运算,是将负整数的低位移出数字,在高位补符号位,即1

    >>> i=-3 # 补码是 1 1 0 1
    >>> j=i>>2 # j的补码 1 1 1 1
    >>> j
    -1
    

    j的补码是1111,由它不能直观地看出其十进制值来,需要将其转换为原码表示,求补码的补码,即得到原码,还记得补码怎么求吗: 先求反码,再加上1就可以了,下面先求上面这个补码的反码:

    补码看做原码 1 1 1 1  符号位是1,表示一个负数
    补码的反码   1 0 0 0  则反码的符号位不变,其余位求反
    补码的补码   1 0 0 1  这就是求出来的原码,还记得吗
                       符号位的1表示符号,最低位的1表示绝对值是1
                        这个原码的十进制表示是 -1  
    
    # 负数的按位求反运算也很容易了
    >>> i = -3 # 补码是1 1 0 1
    >>> j=~i.  # 补码是0 0 1 0 补码的补码是(1) 0 0 1 0丢弃最高位
               # 十进制值是2
    

    题外话: C语言的带符号整数和无符号整数

    不像Python,在一些其他计算机语言中, 内置的整型类型的内存表示是有长度限制的,这个长度限制在制定语言标准时,就规定好了。例如,C语言从创生起到现在为止,经过了3个主要的国际标准阶段,第一阶段简称C89标准,在1989年完成标准化;第二个阶段简称C99标准,在1999年完成标准化;第三个阶段简称C11标准,在2011年完成标准化。严格符合C语言标准的C语言实现(指C语言的编程环境和编译器等)称为标准C语言。MS C(微软的C实现)和GCC(Linux的C实现)是两个主要的C语言实现,它们除了支持标准C语言的功能之外,还对C语言实现了扩展。当编写可移植的C程序时,可移植程度越高,越应当增加使用符合标准C语言的代码。所有C语言实现,在编译C程序时,都提供开关选项,用以在编译标准C语言程序、支持的标准C版本以及扩展C程序之间进行选择。随着C标准的演进,标准C语言支持越来越丰富的类型定义、内置功能和标准库函数。

    在C语言中,整型分为短整型、整型和长整型三种,每种整型又有带符号和无符号两种类型,因此整型有short int、unsigned short int;int、 unsigned int;long int、unsigned long int六种类型,对于这6种整型,C标准支持的最小取值范围又是不同的。为什么说C标准支持的最小取值范围呢? 因为C标准规定了这6种整型的二进制表示的最小位数,C实现可以对此进行扩展,但一般不超过C编程环境所在的设备硬件和操作系统所能支持的自然长度。比如在32位计算机和32位操作系统上,int类型最大可用32位二进制数表示。但C89规定,int类型的二进制表示最小为16位,也就是说,从移植性角度来说,凡是符合C89的C语言编程环境都可安全地假设它支持16位int型整数。

    所谓带符号和无符号整型,其区别是带符号整型的二进制表示中可以带一个符号位,用于表示负数符号,因此带符号整型可以表示正、负整数,多数计算设备中,带符号整型的二进制表示是用补码表示的;而无符号整型则不带符号位,它的所有二进制位都用于表示数字本身,因此无符号整型只能表示正整数,它用原码(对于正整数来说,反码和补码都是原码)表示即可。

    比如,对于用16位二进制数表示的x86系列计算机中,某个用补码表示int型的C实现下:

    int的最大值为: 十进制: 32767 二进制: 0111 1111 1111 1111

    int的最小值为: 绝对值 -32768

    ​ 它的补码的计算法:

    ​ 首先,符号位置1

    ​ 二进制原码 1000 0000 0000 0000 (符号位和最高位占用了同一位),注意: 1111 1111 1111 1111 是-32767

    ​ 取反码: 0111 1111 1111 1111

    ​ 补码=反码+1: 1000 0000 0000 0000

    那么,下面的代码的结果是什么呢:

    int i=32767;
    printf("%d\n", i+1)
    

    从上面的讨论中,我们知道: 32767的二进制表示是0111 1111 1111 1111,由于计算机中的运算都是以二进制方式进行的,对它加1,得到的二进制表示是1000 0000 0000 0000,我们看到,它变成了-32768的补码形式,因此,上面的程序得到的结果是:

    -32768
    

    那么,下面这个程序的结果是什么呢?

    int i=-32768;
    printf("%d\n", i-1);
    

    你总结出规律来了吗?

    C99和C11只是对这些整型所规定的可接受的二进制表示的最小长度有所不同,即这些整型的取值范围有所不同,但原理是一样的。

    对于用16位二进制表示的unsigned int类型,其

    最小值为 0, 二进制为:0000 0000 0000 0000

    最大值为65535, 二进制为: 1111 1111 1111 1111

    3. 比较运算

    运算(操作) 含义
    < 小于
    <= 小于等于
    > 大于
    >= 大于等于
    == 相等
    != 不等于
    is 是相同的对象(1)
    is not 是不同的对象(1)

    (1)is和is not判断两个对象是否是同一个对象。对于值相同的整型对象,Python将不同变量名指向了同一个值(内存块),所以使用is/is not判断分别相当于==/!=判断。

    >>> i=3
    >>> j=3
    >>> i is j
    True
    >>> id(i)   # i和j是同一个对象,所以i is j == True
    10914432
    >>> id(j)
    10914432
    >>> j=4
    >>> i is j
    False
    >>> id(j)    # j和i不是同一个对象,所以i is j == False
    10917536
    

    3.2 布尔类型

    布尔类型是一种特殊的整型。

    3.2.1 取值范围

    布尔类型只有True/False两个值。

    3.2.2 条件判断

    if 语句和while语句对条件进行判断,最简单的条件判断如下所示:

    if True: print("True")
    else: print("False")
        
    while(True):
        print("I am always True")
    

    3.2.3 布尔表达式

    在能够使用布尔变量的地方,都可以使用布尔表达式代替,布尔表达式是对布尔值进行布尔运算得到的。常见的布尔运算如下所示:

    # 布尔或运算
    >>> cond1 or cond2  # or是运算符,cond1和cond2是布尔值
    # 布尔与运算
    >>> cond1 and cond2 # and是运算符,cond1和cond2是布尔值
    # 布尔非运算
    >>> not cond        # not是运算符,cond是布尔值
    

    布尔运算的结果由一个叫做真值表的东东决定:

    布尔或运算

    cond1 cond2 结果
    True True True
    True False True
    False True True
    False False False

    布尔与运算

    cond1 cond2 结果
    True True True
    True False False
    False True False
    False False False

    布尔非运算

    cond 结果
    True False
    False True

    3.2.3 类型转换

    在条件判断和布尔表达式中出现其他类型时,会将其他类型转换成布尔类型使用。其他类型的值向布尔类型的常用转换规则如下:

    1. 0或None或空字符(串)转换成False
    2. 非0值、非None或非空字符(串)转换成True

    布尔类型有时会转换成整型使用,其转换规则很简单:

    1. True转换成1
    2. False转换成0
    >>> bool(1)
    True
    >>> bool(0)
    False
    >>> bool(None)
    False
    >>> bool("")
    False
    >>> bool(1.2)
    True
    >>> bool(-0.1)
    True
    >>> bool(0.0)
    False
    >>> bool("abc")
    True
    >>> int(True)
    1
    >>> int(False)
    0
    

    下面是字符串类型向布尔类型转换的一个例子:

    >>>def checkCond(cond):
    ...    if cond : print("good luck")
    ...    else: print("bad luck")
    
    >>> cond="something"
    >>> checkCond(cond)
    good luck
    >>> cond=""
    >>> checkCond(cond)
    bad luck
    

    3.3 浮点类型

    3.3.1 取值范围

    计算机语言中,使用浮点类型近似表示实数。之所以说是近似表示,是因为由于在内部使用二进制表示我们常用的十进制数,常常存在一些精度的损失。其原因是什么呢? 我们先回顾一下正整数的二进制原码表示,如: 十进制的7,可表示为二进制的111,其转换式如下所示:

    7十进制 = 1x22+1x21+1x20=4+2+1

    那么,二进制实数: 111.11表示什么呢, 很简单,它表示:

    1x22+1x21+1x20+1x2-1+1x2-2=4+2+1+0.5+0.25=7.75

    可见,二进制数111.11可以精确的表示十进制实数7.75

    但是,十进制数0.3能用二进制精确地表示吗,我们看一下:

    2-1=0.5

    2-2=0.25

    2-3=0.125

    2-4 = 0.0625

    2-5 =0.03125

    2-6=0.015625

    我们看到,如果把用二进制小数表示十进制小数看成一个拼图游戏,要表示的十进制数看成最终的结果,用于拼凑这个结果的小积木块就是2-n(n=1, 2, ...),其中最大的小积木块的值是1/2,然后依次是1/4,1/8,1/16,每一级积木块的体积都是上一级的二分之一,由于计算机中浮点数的表示是有长度限制的,这些小积木块的数量是有限的,且由于存在小积木块之间特殊的体积关系,所以使用二进制数并不能精确地表示全部的十进制数,除非这些小积木块能够恰好拼在一起(如下面的0.3125就是两个可以恰好拼在一起的小积木块)。譬如,十进制数0.3可以使用二进制数0.0101近似的表示,其表示的精确的十进制数是

    0.25+0.0625=0.3125

    你也可以用更小的积木块更近似的表示其中的0.0625,达到更近似表示0.3的目的,但是你始终无法准确地表示它。对比整数的表示,在整数中,最小的积木块是0或1,所以只要内存足够大,Python可以精确表示足够大的整数。

    内置的浮点型数据不能精确地表示全部十进制数,是目前的计算机编程语言普遍存在的一个局限。但对于要完成的大多数任务,内置的精度是足够的。对于C语言和C++语言,内置有float和double两种浮点类型,分别称为单精度和双精度浮点类型,它们在计算机中所占的内存单元大小和二进制表示的结构有所不同,因此,后者的精度是前者的两倍,可表示的实数范围也更大。单精度浮点型的精度(有效小数位数)为7位,比如一个单精度浮点数0.12345678901,7以后的数字就是不精确的了。

    与C不一样,Python 3.6中只有双精度浮点数,而没有单精度浮点数。

    3.3.2 浮点数支持的运算

    1. 算术运算

    运算 结果 注释
    x+y x和y的和(sum of x and y)
    x-y x和y的差(difference of x and y)
    x*y x和y的积(product of x and y)
    x/y x和y的商(quotient of x and y) (4)
    x//y x和y的地板商(floor quotient of x and y) (1)
    x%y x/y的余数( remainder of x/y)
    -x 对x取负数(x negated)
    +x x本身(x unchanged)
    abs(x) x的绝对值或称其大小(absolute value or magnitude of x)
    int(x) 将x转换为整型 (3)
    divmod(x,y) 由(x//y, x%y)组成的数值对
    pow(x,y) x的y次方 (2)
    x**y x的y次方 (2)

    (1) 也称为整数除法,对运算结果进行了取整,不过结果的类型不一定是整型(int),结果总是向负无穷方向取整: 1//2=0, (-1)//2=-1, 1//(-2)=-1,而(-1)//(-2)=0。

    (2) 和其他编程语言一样,Python定义pow(0,0)或0**0为1。

    (3) int()是一个类型转换函数,把其他类型的值转换成整型值。从浮点型转换为整型将像C语言中那样对浮点数进行舍入(舍去浮点数中的任何小数,而不是四舍五入)

    (4) 也称为小数除法,结果是一个"精确的"小数

    浮点数还支持下面的算术运算:

    运算 结果
    math.trunc(x) 将x截断为整型数
    round(x[, n]) x舍入为n位有效数字,四舍五入为偶数。假如省略了n,它默认为0
    math.floor(x) 小于等于x的最大整数
    math.ceil(x) 大于等于x的最小整数

    还有更多数值运算,参见math和cmath模块的说明。

    2. 比较运算

    运算 含义
    < 小于
    <= 小于等于
    > 大于
    >= 大于等于
    == 等于
    != 不等于
    is 是相同的对象
    is not 不是相同的对象

    ☺: 上文讲整型,与现在讲浮点数时,谈到它们受到支持的运算是大部分相同的,但它们并不是只支持整型之间或只支持浮点数之间的运算,而是可以支持不同类型的数据之间的运算的。这时,涉及到类型转换的问题,也就是,参与运算的数据,要从等级较低的类型转换为等级较高的类型,如: 对于整型和浮点型的混合运算,整型数先要转换成浮点数,例子代码如下:

    # cal1.py
    i=3
    f=4.2
    r=i+f     # 等价于 r=float(i)+f
    print(r)
    

    运行结果是

    $ ./cal1.py
    $ 7.2
    

    到目前为止我们学过的内置数据类型: 布尔型、整型和浮点型,参与混合运算时的等级从低到高依次是: 布尔型、整型、浮点型。举个例子:

    # cal2.py
    b=True
    i=3
    f=4.2
    r=b+i+f  # 等价于r=float(b)+float(i)+f
    print(r)
    

    结果是:

    $ ./cal2.py
    $ 8.2
    

    相关文章

      网友评论

        本文标题:Python中的数值类型

        本文链接:https://www.haomeiwen.com/subject/nzlqgxtx.html