查阅材料,分析浮点数在计算机中的处理方式
1.那么计算机是如何处理小数的呢?
历史上计算机科学家们曾提出过多种解决方案,最终获得广泛应用的是 IEEE 754 标准中的方案,目前最新版的标准是 IEEE std 754-2008。该标准提出数字系统中的浮点数是对数学中的实数(小数)的近似,同时该标准规定表达浮点数的 0、1 序列被分为三部分(三个域):sign,exponent,fraction
以32位单精度浮点数为例(float),其具体的转换规则是:首先把二进制小数(补码)用二进制科学计数法表示,比如上面给出的例子 1111101.001=1.111101001×261111101.001=1.111101001×26。符号位sign表示数的正负(0为正,1为负),故此处填0。exponent表示科学计数法的指数部分,请务必注意的是,这里所填的指数并不是前面算出来的实际指数,而是等于实际指数加上一个数(指数偏移),偏移量为2^(e-1)-1,其中e是exponent的宽度(位数)。对于32位单精度浮点数,exponent宽度为8,因此偏移量为127,所以exponent的值为133,即10000101。之后的fraction表示尾数,即科学计数法中的小数部分11110100100000000000000(共23位)。因此32位浮点数125.125D在计算机中就被表示为01000010111110100100000000000000。
对于32位单精度浮点数(float),sign是1位,exponent是8位(指数偏移量是127),fraction是23位。对于64位双精度浮点数(double),sign是1为,exponent是11位(指数偏移量是1023),fraction是52位。
需要指出的是125.125D的转换结果实际上是规约形式的浮点数,即exponent的数值大于0且小于2^e-1,默认科学计数法中整数部分为1,因此尾数只保留了小数部分。但当数值非常接近于0时,可能出现exponent的数值等于0,且科学计数法中整数部分为0的情况,这就称为非规约形式的浮点数。对此IEEE std 754-2008规定:非规约形式浮点数的exponent值等于同种情况下规约形式浮点数的exponent再加1。比如exponent=1,显然这是规约形式浮点数,其实际指数应该是-126(1-127);而exponent=0,这是非规约形式浮点数,(若按照规约形式浮点数计算,其实际指数应为-127(0-127))那么根据前面提到的标准可知这个非规约形式浮点数的实际指数也是-126。所有的非规约浮点数比规约浮点数更接近0。
对于二进制小数,长度为 kk 时的最大值为 1−2−k1−2−k,好比对于二进制整数,长度为 kk 时的最大值为 2k−12k−1,如:
· 一位时:0.1B=1−2−1=.50.1B=1−2−1=.5
· 二位时:0.11B=1−2−2=0.750.11B=1−2−2=0.75
· 四位时:0.1111B=1−2−4=0.93570.1111B=1−2−4=0.9357
对于32位单精度浮点数而言,最大的非规约数是 (1−2−23)∗2−126≈1.18∗10−38(1−2−23)∗2−126≈1.18∗10−38,最小的非规约数是 2−23∗2−126=2−149≈1.40∗10−452−23∗2−126=2−149≈1.40∗10−45。对于 64 为双精度浮点数而言,最大的非规约数是 (1−2−52)∗2−1022≈2.22∗10−308(1−2−52)∗2−1022≈2.22∗10−308,最大的非规约数是 2−52∗2−1022≈4.94∗10−3242−52∗2−1022≈4.94∗10−324。
由上面的内容可以知道,浮点数能表示的范围其实是有限的,它只能表示整条数轴中的三部分:
· 某个很大的负数到某个很接近于0的负数、
· 0、
· 某个很接近于0的整数到某个很大的正数。
此外,由数学分析的知识可知实数是“稠密”(dense)的,可以证明在任意两个不相等的实数之间总有无穷多个两两不等的实数;但浮点数不是这样,浮点数是“稀疏”的,两个浮点数之间只有有限个浮点数,并且两个“相邻”的浮点数之间的距离可能是巨大的,这就会带来精度方面的一系列问题。
譬如两个“相邻”的32位单精度浮点数,它们的符号位和指数位都相同,尾数位的前22位都相同,只有最后一位相差1,那么这两个浮点数之间的差值可能是非常惊人的。例如01111110100000000000000000000001和01111110100000000000000000000000(指数部分,253-127=126),在32位单精度情况下,它们是“相邻”的,但它们之间的差值竟高达1.014*10^31。换句话说,在32位单精度浮点数中,处于这段差值以内的数都无法表示。如果以相对误差来讨论的话,32位单精度浮点数的尾数只有23位,第24位及其后的值会被舍入,可以近似认为其相对误差为 2−23≈1.20∗10−72−23≈1.20∗10−7。这对于某些需要上亿甚至百亿次迭代的程序而言是无法接受的。而64位双精度浮点数的相对误差可以近似认为是 2−52≈2.22∗10−162−52≈2.22∗10−16,比32位单精度浮点数的精度高出不少。可见,64位双精度浮点数不仅表示数的范围扩大了,而且它所刻画的浮点数分布更加“细密”,相对误差更小。并且,对于64位线宽度的计算机而言,处理64位双精度浮点数与处理32位双精度浮点数所需的开销相同,并不需要额外的循环移位,因此还是建议使用64位双精度浮点数。
当然,浮点数位数越多,其相对误差也就越小,只要它的精度满足程序运行需要就可放心使用。但无论如何,浮点数终究只是实数的粗糙近似,浮点数不可能完全刻画实数,因为浮点数的位数终究是有限的,换句话说它所能表示的总是有限个有理数,而根据数学分析的知识,在实数轴中虽然无理数和有理数都是无限多的,但无理数集是不可数的,而有理数集却是可数的。
除了上面的内容以外,在编程中需要特别注意的有两点:
一、浮点数都是带符号的,不存在unsigned double和unsigned float;
二、两个浮点数之间不能用==来判断是否相等,因为浮点数是对实数的近似,所以计算机中两个浮点数不可能完全相等,最多也只能保证其差值小于用户规定的误差限度。
--------------------- 本文来自 Inside_Zhang 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/lanchunhui/article/details/50662458?utm_source=copy