美文网首页
float类型精度问题解读

float类型精度问题解读

作者: 于天佐 | 来源:发表于2018-07-09 18:04 被阅读0次

    问题

        项目中如果有处理价格的需求那么很大概率要用到float类型表示一个小数,那么如果定义一个比较高的精度边界条件常常会遇到精度不足的情况,这篇文章就来讨论一下这个问题。看一下程序中调试的截图:


    float_test.jpg

    一个三位小数部分999的float类型变量,我们初始化值和实际值如图所示。


    float_test2.jpg
    一个8为小数部分99999999的float类型变量,我们初始化值和实际值如图所示。
        我们看到第一张截图显示变量的实际值是一个接近于1.999的四舍五入的小数;第二张截图实际值干脆丢失了所有精度。那么是什么导致了这样情况的呢,下面来仔细分析一下float类型变量。

    float类型内存模型

        首先来看一下float类型在内存中的模型。flaot类型变量在内存中占用32个二进制位。它的内存排布如下图所示:


    float_mem.jpg

        上图最左边是高位,最右边是低位。flaot类型变量分成三个部分:第一部分是最高1位,它代表符号位,0表示正值,1代表负值;第二部分高1位后的8位,这个8位代表指数部分值(2的x次方);第三部分是剩余23位,它代表二进制科学记数法表示的实际小数点后面的值。这三个部分又被称为符号位(sign bit),指数偏移值(exponent bias),分数值(fraction)。下图代表他们在运算中的关系:


    floatvalue.jpg
        先举一个实际栗子整体看一下三部分如何拼装出一个float值,然后再具体说一下比较难以理解的第二部分指数偏移值和第三部分分数值。先看一个简单的栗子,1.25。先给出它的内存排布:高1位0(正数),随后8位(0111 1111),随后23位(010000...0)。第一部分0表示正数,第二部分先算出它代表的十进制数是127,这里规定指数偏移值是n(指数实际值) + 127的值,所以这里实际指数值应该是127-127=0,也就是2的0次幂,实际上这就是2进制科学计数法的移动位数,在这里刚好是不用移位;我们接着看第三部分,第三部分是2进制科学记数法的小数点后面的值,由于是科学记数法,所以第一位一定是1,故而省略,那么这里的科学记数法小数就成为了1.010000...0;结合第二部分移位0,所以整数部分是1(二进制),小数部分0.01(二进制),那么这个数的最终值就是(2的1次幂) + (2的-2次幂)= 1.25。
        那么它的内存布局是怎么算出来的呢,尤其是第二部分和第三部分的值是如何得来的呢?再看一个栗子,-8.75,我们这一次从头推算一下。首先符号位是1,8.75分别在小数点两侧换算成2机制数,变成1000.11,这个小数变成科学记数法1.00011 * 2的3次幂,所以第二部分实际指数值为3,加上127就是内存中真正的指数偏移值即130(二进制1000 0010);第三部分是科学记数法的小数部分,所以是00011000...0。所以最终的二进制值为1 10000010 00011000...0。

        通过上面两个栗子,我想应该可以很清晰的转化一个float值到2进制值了,那么接下来我们看一下float值的边界,即所能表示的最大数和最小数。按照上面规则套一下,最大的值首先是正数,其次指数部分最大偏移值为255(全部8为1),实际指数为255-127=128,即2的127次幂,剩余科学计数法小数部分全为1,这样这个数应该就是一个32位数所能表示的最大数了。实际中float类型所能表示的最大数跟上面的推算有出入,主要是在指数部分,指数偏移在IEEE规定中最大数指数偏移只能是254(1111 1110),指数偏移255(1111 1111)是有其他用途的。同样最小数的约定也是不能指数偏移为0,最小为1。这个规范可以看一下下面的表格:


    floatbianjie.jpg
        上面表格已经很清楚的表示了flaot类型数的合法值范围,那么是不是在最大数和最小数之间的所有小数都可以用float表示呢?答案是精度无法保证。回到开头的程序实验,1.999为什么实际值不是999呢?通过上面的规则推算, 我们应该可以看出小数部分的值是通过2的-n次幂累加而来(即0.5,0.25,0.125.......),这些数累加起来近似的表示一个小数,如果小数部分刚好满足2的-n次幂或者其和,那么精度就会完美表达,如果不能(比如.999)那么就只能表示为一个近似的值,让其尽量接近我们想要的值,这也正是第一个程序实验截图中程序中实际的值并不是.999。那么再看小数部分8个9为什么无法表达呢?我们先看一下float所能表达的最小精度,即第三部分为000...01,这个二进制小数部分即2的-23次幂,换算成十进制是0.000000119209,我们可以看到float能表达的小数部分最小精度到了小数点后第7位,我们需要的小数点第8位float已经无法通过近似模拟达到了,所以已经超出了float类型的精度极限,故而程序的第二张截图实际值无法表达精度了。
        以上就是float类型精度问题的一个整体分析,希望对有过此类困惑的朋友有所帮助。

    相关文章

      网友评论

          本文标题:float类型精度问题解读

          本文链接:https://www.haomeiwen.com/subject/loizuftx.html