Python浮点数

在编程语言中, 小数通常以浮点数的形式存储. 浮点数和定点数是相对的: 小数在存储过程中如果小数点发生移动, 就称为浮点数; 如果小数点不同, 就称为定点数.

Python中的小数有两种数学形式:

  • 十进制形式

这种就是平时我们看到的小数形式, 例如0.123, 12.3, 123.0

书写时必须包含一个小数点, 否则会被Python当作整数处理.

  • 指数形式

Python小数的指数形式的写法为:

aEn 或 aen

a为尾数部分, 是一个十进制数; n为指数部分, 是一个十进制整数; Ee是固定的字符, 用于分割尾数部分和指数部分. 整个表达式等价于 \(a*10^n\).

Exmple:

  • \(2.1E5 = 2.1*10^5\), 其中2.1是尾数, 5是指数.
  • \(3.7E{-2} = 3.7*10^{-2}\), 其中3.7是尾数, -2是指数.
  • \(0.5E7 = 0.5*10^7\), 其中0.5是尾数, 7是指数.

注意, 只要写成指数形式就是小数, 即使它的最终值看起来像一个整数. 例如, 14E3等价于14000, 但14E3是一个小数.

Python中只有一种小数类型, 就是float. C语言有两种小数类型, 分别是floatdouble: float能容纳的小数范围较小, double能容纳的小数范围较大.

浮点数精度问题

Python中浮点数类型之间的运算, 其结果并不像我们想象的那样, 例如:

>>> 0.1+0.2
0.30000000000000004
>>> 0.1+0.1-0.2
0.0
>>> 0.1+0.1+0.1-0.3
5.551115123125783e-17
>>> 0.1+0.1+0.1-0.2
0.10000000000000003

为什么在计算这么简单的问题上, 计算机会出现这样的低级错误呢? 真正的原因在于十进制数和二进制数的转换.

在计算机中, 所有的数据都是以二进制形式存储的. 当我们以十进制数进行运算的时候, 计算机需要将各个十进制数转换成二进制数, 然后进行二进制间的计算.

以类似0.1这样的浮点数为例, 如果手动将其转换成二进制, 其结果为:

\[0.1_{(10)} = 0.00011001100110011..._{(2)}\]

可以看到, 结果是无限循环的, 也就是说, 0.1转换成二进制数后, 无法精确到等于十进制数0.1. 同时, 由于计算机存储的位数是有限的, 所以如果要存储的位数超过了计算机存储位数的最大值, 其后续位数会被舍弃.

注解

这种问题不仅在Python中存在, 在所有支持浮点数运算的编程语言中都会遇到.

如果需要非常精确的结果, 可以使用decimal模块, 它实现的十进制数运算适合会计方面的应用和高精度要求的应用.

Exmaple:

import decimal

a = decimal.Decimal("10.0")
b = decimal.Decimal("3")

print(10.0/3)
print(a/b)

运行结果位:

3.3333333333333335
3.333333333333333333333333333

可以看到, 相比普通运算的结果, 使用decimal模块得到的结果更加精确.

如果decimal模块还是无法满足需求, 还可以使用fractions模块.

Example:

from fractions import Fraction

print(10/3)
print(Fraction(10, 3))

运行结果:

3.3333333333333335
10/3

可以看到, 通过fractions模块能很好地解决浮点类型数之间的运算问题.