C/C++ python float、double为什么浮点计算不准确? 表示范围及精度

现象


>>> 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绝对值的最大值取到2127 *(2-2-23)约等于2128=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的阶码(移码表示)为02047,偏移量为1024,故指数范围为-10241023,得表示范围为(21023*2)~(-21023*2)即为-1.7E+308~1.7E+308,绝对值最小可以取到2-1024,精度则为252-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(1038),1.17549e-038(10^-38);

double的最大值和最小值分别为1.79769e+308(10308),2.22507e-308(10-308)。

https://blog.csdn.net/black_kyatu/article/details/79257346

ref
https://blog.csdn.net/zhouxufeng1996/article/details/94999514?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task

发布了415 篇原创文章 · 获赞 251 · 访问量 68万+

猜你喜欢

转载自blog.csdn.net/qq_35608277/article/details/104668553