2.2.3 浮点数类型 float

浮点数就是包含小数点的数,大体对应于数学中的实数集合。现实世界中的职工工资(以 元为单位)、房屋面积(以平方米为单位)、人的身高(以米为单位)、圆周率等在程序中都 适合用浮点数表示。

Python 语言提供了类型 float 用于表示浮点数。float 类型的字面值形式与数学中的 写法基本一致,但是允许小数点后面没有任何数字(表示小数部分为 0),例如下列字面值 都是浮点数:

3.1415 -6.78 123.0 0\. -6.

Python 为浮点数类型提供了通常的加减乘除等运算,运算符与整数类型是一样的(见 表 2.1)。但是,与整数类型不同的是,运算符“/”用于浮点数时,是要保留小数部分的, 例如:

>>> 11.0 / 3.0
3.6666666666666665

没错,最后一位小数是 5 而不是 6!原因见下面关于浮点数内部表示的内容。 将一个浮点数赋值给变量,则该变量就是 float 类型(实际上是指向一个 float 类型的数据)。例如:

>>> f = 3.14
>>> type(f)
<type 'float'>

浮点数运算同样可以和变量赋值结合起来,形成如表 2.2 所示的简写形式。

浮点数的能力与限制 浮点数类型能够表示巨大的数值,能够进行高精度的计算。但是,由于浮点数在计算机

内是用固定长度的二进制表示的,有些数可能无法精确地表示,只能存储带有微小误差的近 似值。例如,

>>> 1.2 – 1.0
0.19999999999999996

结果比 0.2 略小。又如:

>>> 2.2 – 1.2
1.0000000000000002

结果比 1.0 略大。然而,下面这个表达式却计算出了精确结果:

>>> 2.0 – 1.0
1.0

尽管浮点表示带来的这种微小误差不至于影响数值计算实际应用,但在程序设计中仍然 可能导致错误。例如,万一某个程序中需要比较 2.2 ? 1 是否等于 1.2,那我们就得不到 预期的肯定回答,因为 Python 的计算结果是不相等!请看下面两个比较式:

>>> (1.2 – 1.0) == 0.2
False
>>> (2.0 – 1.0) == 1.0
True

先解释一下,上例中用到了比较两个表达式是否相等的运算符“==”,另外显示结果出 现了表示真假的布尔值 True 和 False,这些内容在后面布尔类型一节中有详细介绍。从 这个例子我们得到一条重要的经验:不要对浮点数使用==来判断是否相等。正确的做法是 检查两个浮点数的差是否足够小,是则认为相等。例如:

>>> epsilon = 0.0000000000001
>>> abs((1.2 – 1.0) - 0.2) &lt; epsilon
True

另外从运算效率考虑,与整数类型 int 相比,浮点数类型 float 的运算效率较低,由 此我们得出另一条经验:如果不是必须用到小数,那就应当使用整数类型。

科学记数法

对于很大或很小的浮点数,Python 会自动以科学记数法来表示。所谓科学记数法就是

以“a×10 的整数次幂”的形式来表示数值,其中 1 <= abs(a) < 10。例如,12345 可 以表示成 1.2345e+4,0.00123 可以表示为 1.2345e-3。下面是 Python 的计算例子:

>>> 1234.5678 ** 9
6.662458388479362e+27
>>> 1234.5678 ** -9
1.5009474606688535e-28

正如 int 不同于整数集 I 一样,Python 的 float 也不同于实数集 R,因为 float 仍 然只能表示有限的浮点数。当一个表达式的结果超出了浮点数表示范围的时候,Python 会 显示结果为 inf(无穷大)或-inf(负无穷)。读者可以做一个有趣但略显麻烦的实验,试 一试 Python 最大能表示多大的浮点数。下面是本书著者所做的实验结果,可以看到,最大 浮点数的数量级是 10308,有效数字部分已经精确到小数点后面第 53 位(Python 在显示结果 时只保留小数点后 16 位),当该位为 6 时是合法的浮点数,当该位为 7 时则超出范围。

>>> 1.79769313486231580793728971405303415079934132710037826e+308
1.7976931348623157e+308
>>> 1.79769313486231580793728971405303415079934132710037827e+308
inf

顺便说一下,如果读者做这个实验,相信你一定会采用一种快速有效的策略来确定每一 位有效数字,而不会对每一位都从 0 试到 9。例如,当发现 1.7…1e+308 是合法的浮点数, 而 1.7…9e+308 超出了范围,接下去应当检查 1.7…5e+308 的合法性。这种方法就是本 书后面算法设计一章中介绍的二分查找策略。我们在第 1 章说过,计算思维人人皆有、处处 可见,不是吗?

自动类型转换

float 类型与 float 类型的数据相互运算,结果当然是 float 类型。问题是 float 类型能与 int 或 long 类型进行运算吗?

由于整数、长整数和浮点数都是数值(在数学上都属于实数集合 R),因此 Python 允许

它们混合运算,就像 int 可以与 long 混合运算一样。Python 在对混合类型的表达式进行 求值时,首先将 int 或 long 类型转换成 float,然后再执行 float 运算,结果为 float 类型。例如:

>>> type(2 + 3.0)
<type 'float'>
>>> type(2 + 3L * 4.5)
<type 'float'>

手动类型转换

除了在计算混合类型的表达式时 Python 自动进行类型转换之外,有时我们还需要自己 手动转换类型。这是通过几个类型函数 int()、long()和 float()实现的。例如,当我 们要计算一批整型数据的平均值,程序中一般会先求出这批数据的总和 sum,然后再除以数 据的个数 n,即:

average = sum / n

但这个结果未必如我们所愿,因为 sum 和 n 都是整数,Python 执行的是整数除法,小数部 分被舍弃了,导致结果误差太大。为解决此问题,我们需要手动转换数据类型:

average = float(sum) / n

其中 float()函数将 int 类型的 sum 转换成了 float 类型,而 n 无需转换,因为 Python 在计算 float 与 int 混合的表达式时,会自动将 n 转换成 float 类型。

要注意的是,下面这种转换方式是错误的:

average = float(sum/n)

因为括号里的算式先计算,得到的就是整除结果,然后再试图转换成 float 类型时,已经 为时已晚,小数部分已经丢失了。

其实,调用类型函数来手动转换类型并不是好方法,我们有更简单、更高效的做法。如 果已知的数据都是整数类型的,而我们又希望得到浮点类型的结果,那么我们可以将表达式 涉及的某个整数或某一些整数加上小数点,小数点后面再加个 0,这样整数运算就会变成浮 点运算。例如求两个整数的平均值:

>>> x = 3
>>> y = 4
>>> z = (x + y) / 2.0
>>> z
3.5

例中我们人为地将数据个数 2 写成了 2.0,这样就使计算结果变成了 float 类型。 当然,在将浮点数转换成整数类型时,就没有这种简便方法了,只能通过类型函数来转换。例如:

>>> int(3.8)
3
>>> long(3.8)
3L

可见,float 类型转换成 int 或 long 时,只是简单地舍去小数部分,并没有做四舍五入。 如果希望得到四舍五入的结果,一个小技巧是先为该值(正数)加上 0.5 再转换。更一般 的方法是调用内建函数 round(),它专门用于将浮点数转换成最接近的整数部分。不过舍 入后的结果仍然是 float,为了得到 int 类型的数据还需要再用 int()转换。例如:

>>> round(3.14)
3.0
>>> round(-3.14)
-3.0
>>> round(3.5)
4.0
>>> round(-3.5)
-4.0
>>> int(round(-3.14))
-3