float类型精度问题解读

问题

    项目中如果有处理价格的需求那么很大概率要用到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类型精度问题的一个整体分析,希望对有过此类困惑的朋友有所帮助。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,133评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,682评论 3 390
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,784评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,508评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,603评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,607评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,604评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,359评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,805评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,121评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,280评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,959评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,588评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,206评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,442评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,193评论 2 367
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,144评论 2 352

推荐阅读更多精彩内容