关于浮点型精度问题的一些理解

这是一篇关于浮点型精度的文章,大致会从三个大的问题入手,去解析精度问题。

字符串转浮点型出现精度丢失

浮点型和浮点型操作精度丢失

NSDecimalNumber如何解决精度丢失

前言

4月21号终于完成了4月份的自考,也有些时间来研究一些东西,写写博客了,刚好群里边有人问关于浮点型精度的问题,我去研究了一些资料,问了一些前辈和同行,总结如下,结尾处会有我参考资料的链接。(以下内容均是基于32位系统进行描述的)

字符串在内存中的存储方式

首先,我们了解一下字符串的存储方式,它是以ASCII码进行存储的,然后将对应的ASCII码转换成二进制存储在内存中。

例如:a对应的ASCII码是97,97对应的七位二进制表示是1100001,八位是01100001,这两者区别可以忽略掉,八位二进制可表示的字符范围更宽泛而已。因为是一个字符所以只需要1个字节,也就是8位,也就是1组二进制。

1.23作为一个字符串它由4个字符组成所以需要4个字节,也就是32位,需要4组二进制。它们分别对应的ASCII码是49、46、50、51,转换成二进制就分别是110001、101110、110010、110011。

浮点型在内存中的存储方式

浮点型作为一种与整型不同的存储方式,它是遵循IEEE754标准的。浮点型在内存中的存储分为了3个部分,分别是1个符号位s,8个指数E,23个有效数字M。任意一个二进制数V都可以写成sM*2^E,s就是正号和负号,M就是有效数字,E就是指数。这个公式的意思是将M的小数点向右移E位。下面是IEEE754的一些规定:

1

2

3

4

5

1. 当s=0,V为正数;当s=1,V为负数。

2. M表示有效数字,大于等于1,小于2。

3. 2^E表示指数位。

4. E的真实值必须再减去一个中间数,对于8位的E,这个中间数是127。

5. 在计算机内部保存M时,默认这个数的第一位总是1,因此可以被舍去,只保存后面的xxxxxx部分。

例如浮点数10.0,转换成二进制是1010,在32位系统下你还要在前面补上28个0,这个二进制并不是在内存中的存储样式。套用上面的公式你可以写成+1.010*2^3,其中s是0,那么也就是+,M就是1.01,E就是3。这只是公式,并不是在内存中的真正存储形式。接下来看下下面的图,图中的数值不用看,跟本文无关,主要是看组成的三部分。


看过了上图,还是以+1.010*2^3为例,将其填入该32位的二进制中。

首先是符号位S,也就是图中的sign,因为是正号,所以sign是0;

E是3,按第4条规则,E的真实值需要E+127,也就是130,将其转成二进制存储,也就是10000010;

M是1.010,按上述第5条规则,舍去1,也就是01000000000000000000000;

所以合在一起,浮点数10.0在内存中的表示就是01000001001000000000000000000000。

整型在内存中的存储方式

既然浮点型和字符串在内存中的存储方式都说了,顺道提下整型在内存中的存储方式。

这个很简单,它只需要考虑一个问题,就是正数和负数,它由两部分构成,第一个还是符号位,表示正负,后面31位都是实际存储的数字,所以它支持存储的数字范围是-2^31~~~2^31-1。只需要直接将整数转换成二进制就可以了。

比如100,在内存中的二进制表示就是00000000000000000000000001100100。

字符串转浮点型出现精度丢失

前面说了字符串的存储方式和浮点型的存储方式,现在这个问题其实挺好解决的了。对了,还要说的一点是,你用的什么方法进行强转的,对于NSString类型的转成float,一般使用的是floatValue方法,那么可以看下官方文档对这个方法的解读。

1

2

3

The floating-point value of the string as a float.

This property doesn’t include whitespace at the beginning of the string. This property is HUGE_VAL or –HUGE_VAL on overflow, 0.0 on underflow. This property is 0.0 if the string doesn’t begin with a valid text representation of a floating-point number.

This method uses formatting information stored in the non-localized value; use an NSScanner object for localized scanning of numeric values from a string.

上文比较有用的信息就是这个方法是通过NSScanner对字符串进行逐个扫描,如果不是一个真正的浮点型,比如@”abv”这种,这个方法就是0.0;如果是@”1.23”这种,它就会转化成浮点型1.23。所以可以排除掉这个可能:浮点型和字符串在内存中的二进制表现形式不同而导致的。

那么,问题就很清楚了,肯定是浮点型自身存储成2进制的时候发生了精度丢失。这次举两个例子对比下:

浮点数10.0,它的有效数字M是1.010,忽略掉整数位1,实际存储的也就3位是010,在32位情况下,M最多可以存储23位有效数,所以它是无损的。

浮点数1.2,它的有效数字是0011001100110011001100110011001100110011001100110011,这个长度大大超过了23位能存储的,所以它会被截取掉后面超出的部分,超出部分明显不全是0,所以会对它的精度造成损失。

总结:精度损失不损失需要看十进制的数据能否精确的转换为二进制。

浮点型和浮点型操作精度丢失

关于这个问题,其实看懂了上面的内容,就知道这个问题出在哪里了。比如a=b+c;首先浮点型b和c自身存储就已经损失精度了,其次得到的结果a如果也是一个不能精确转为二进制的浮点型,那么必然造成精度的2次缺失,会跟你想象中的结果差距更大。

NSDecimalNumber如何解决精度丢失

找到了问题的产生原因,再来说说iOS提供的NSDecimalNumber这个类如何解决这个问题。我们先来看下系统文档:

1NSDecimalNumber, an immutable subclass of NSNumber, provides an object-oriented wrapper for doing base-10 arithmetic. An instance can represent any number that can be expressed as mantissa x 10^exponent where mantissa is a decimal integer up to 38 digits long, and exponent is an integer from –128 through 127.

上述大概意思是NSDecimalNumber是NSNumber的一个子类,它提供了一个基于10进制面向对象的封装。它也有一个类似IEEE754的公式:N*M*10^E

例如浮点型1.23,套用上面公式就是1*123*10^(-2)。它会将这个浮点型包装成一个NSDecimalNumber对象,N代表是正数还是负数,在本例中是1,M代表将浮点型转化为整数后的数,在本例中是123,E代表指数,在本例中是-2。

这样来看,系统的处理方式其实很明显了,这个类不存储浮点型,只存储整数,自然避免了IEEE754那种方式的精度损失,当然类在内存中肯定跟浮点型是不同的,但是M这个属性保存的是整数型,是同浮点型一样都是基础数据类型。

后续

其实还有一个整型强转浮点型的问题,这个问题跟字符串转浮点型的情况还是不同的,这个是因为两者对二进制的转化方式不同。整型存储在内存中是直接转化成二进制的,浮点型虽然也是转成二进制存储,但它需要符合IEEE754标准,所以你直接把整型的二进制取出来当浮点型使用,肯定会出现对应的问题。

原文链接: 关于浮点型精度问题的一些理解 | YownYang's blog

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

推荐阅读更多精彩内容