现象

>>> 1.2 - 1.0

0.19999999999999996

原因:

根本原因:存在(用二进制存储时)“不可表示”,如0.1,0.2和0.01

计算机会把你心里想的十进制小数转换为二进制小数,然后在内存中存储二进制小数

CPython 中的 float 类型使用C语言的 double 类型进行存储。 float 对象的值是以固定的精度(通常为 53 位)存储的二进制浮点数,由于 Python 使用 C 操作,而后者依赖于处理器中的硬件实现来执行浮点运算。 这意味着就浮点运算而言,Python 的行为类似于许多流行的语言,包括 C 和 Java。

许多可以轻松地用十进制表示的数字不能用二进制浮点表示。例如,之后:

1.0011001100110011001100110011001100110011001100110011 (binary)

确切地说:

>>> x = 1.2

1.1999999999999999555910790149937383830547332763671875 (decimal)

在python中,浮点数有53位可用精度,典型的 53 位精度为 Python 浮点数提供了 15-16 位小数的精度。

解决

decimal模块提供了一个数据类型Decimal用于浮点数计算,相比内置的浮点数类型float,Decimal这个类型有助于需要精确十进制表达的场合,或者用户希望计算结果与手算相符的场合。Decimal重现了手工的数学运算,这就确保了二进制浮点数无法精确保有的数据精度。

>>> from decimal import Decimal
>>> from decimal import getcontext
>>> Decimal('4.20') + Decimal('2.10')
Decimal('6.30')
>>> from decimal import Decimal
>>> from decimal import getcontext
>>> x = 4.20
>>> y = 2.10
>>> z = Decimal(str(x)) + Decimal(str(y))
>>> z
Decimal('6.3')
>>> getcontext().prec = 4 #设置精度
>>> Decimal('1.00') /Decimal('3.0')
Decimal('0.3333')

具体范围和 精度原理

浮点数存储方法

首先要搞清楚浮点数在内存中的存储方式。浮点数,区别于定点数,指的是

小数点位不确定

的的数据类型,其原理是将一个浮点数a用两个数m(尾数)和e(指数)来表示:a = m × b^e。其中的b为选取的基数。科学计数法就是一种特殊形式的浮点数。

在计算机二进制表示中,浮点数采用

2作为基数,规定尾数的范围为1.0~2.0之间

以float类型为例,根据最广泛采用的IEEE754标准规定,

float数据类型长度为32位,其中最高位为符号位,中间8位为指数位,最后23位作为尾数位。

符号位

最高位符号位通过0/1来区分正负,0正1负;

指数位

指数位则规定采用移码的形式存储,这样可以保证指数部分为无符号数,方便比较大小。移码表示法是在数X上增加一个偏移量来定义的,如果机器字长为n,规定偏移量为2^(n-1),对于8位补码-128~ 127,可得到对应的阶码表示为0 ~ 255,其中0和255分别用来表示0和无穷大,1~ 254用来表示规范数字,即

指数范围从-126到127

尾数位

尾数部分统一规定为

1.0-2.0之间

,最高位必然为1,故可以省略,所

以尾数部分从小数点后

算起,

最小可以取到1(小数点位全为0),最大则取到二进制1.1…1(小数点后23位全为1)

,即取到(2减去2

-23

),可近似约等于2。

范围

故得到float绝对值的最大值取到2

127

*(2-2

-23

)约等于2

128

=3.4E+38。加上符号之后可得float表示范围为(-3.4E+38)~(3.4E+38)。当然实际是取不到的,开区间。绝对值最小则可以取到2^-127 *1,即为1.175E-38。

精度

接下来解释精度。由于尾数部分位数是固定的小数点后23位,

23位所能表示的最大数是2^23−1=8388607

,所以十进制的尾数部分最大数值是8388607,也就是说尾数数值超过这个值之后,float将无法精确表示,

所以float最多能表示小于8388607的小数点后7位

,但

绝对能保证的为6位

,也即float的十进制的精度为为6~7位。

double

double数据类型的推算过程和上述同理,唯一的区别在于尾数由23位扩展到52位,阶码由8位增加到了11位,计算方法不变。所以double的阶码(移码表示)为0

2047,偏移量为1024,故指数范围为-1024

1023,得表示范围为(2

1023*2)~(-2

1023*2)即为-1.7E+308~1.7E+308,绝对值最小可以取到2

-1024

,精度则为2

52

-1=4503599627370495,为16位。所以精度最高位16位,一定可以保证15位。

plus

int和long都是用32位来存储最大值和最小值分别为2147483647(2^31-1 ~ 10^9), -2147483648(-2^31);

long long 是用64位来存储最大值和最小值分别为9223372036854775807(10^18),-9223372036854775808;

float的最大值和最小值分别为3.40282e+038(10

38

),1.17549e-038(10^-38);

double的最大值和最小值分别为1.79769e+308(10

308),2.22507e-308(10

-308)。

ref