iOS开发浮点数计算精度问题

1、浮点数运算带来的问题

CGFloat badnum = 1.05f;
NSLog(@"badnumX100 = %f",badnum*100);
//输出
//badnumX100 = 104.999995  

在日常工作中涉及到浮点数(float、double)的运算

2、浮点数运算精度的解决方案

NSDecimalNumber的实现

数字19.99表示方法
#define NSDecimalMaxSize (8)
    // Give a precision of at least 38 decimal digits, 128 binary positions.

#define NSDecimalNoScale SHRT_MAX

typedef struct {
    signed   int _exponent:8;//幂指数
    unsigned int _length:4;     // length == 0 && isNegative -> NaN
    unsigned int _isNegative:1;//符号
    unsigned int _isCompact:1;
    unsigned int _reserved:18;
    unsigned short _mantissa[NSDecimalMaxSize];//存储数据
} NSDecimal;

使用NSDecimalNumber进行浮点数的运算

    //100.0转化成NSDecimalNumber
    NSDecimalNumber *g_100 = [NSDecimalNumber decimalNumberWithString:@"100"];
    //1.05转化成NSDecimalNumber
    NSDecimalNumber *g_105 = [NSDecimalNumber decimalNumberWithString:@"1.05"];
    //两个数相乘 1.05X100
    NSDecimalNumber *goodnum = [g_105 decimalNumberByMultiplyingBy:g_100];
    NSLog(@"goodnum 1.05X100 = %@",goodnum);

    //输出
    //goodnum 1.05X100 = 105

浮点数判等

由于浮点数内部存储地不精确,在比较两个浮点数是否相等时,不能简单地使用 == 符号来判断。
判断两个浮点数 A, B 是否相等,需要转化成求这两个浮点数差的绝对值 C,即 C = fabs(A - B),然后看这个值 C 是否小于一个极小数。
如果小于一个极小数,则可以认为这两个浮点数是相等的。
根据实际工程中的需要,通常这个极小数的参考值是 1e-6 或 1e-8 。

3、浮点数在计算机中的存储方式导致精度问题

浮点数在计算机中的存储方式

不论是 float 类型还是 double 类型,在存储方式上都是遵从IEEE的规范:

float 遵从的是 IEEE R32.24;double 遵从的是 IEEE R64.53;

单精度或双精度在存储中,都分为三个部分:

符号位 (Sign):0代表正数,1代表为负数;
指数位 (Exponent):用于存储科学计数法中的指数数据;
尾数部分 (Mantissa):采用移位存储尾数部分;

单精度和双精度的存储方式.png

R32.24 和 R64.53 的存储方式都是用科学计数法来存储数据的,比如:

8.25 用十进制表示为:8.25 X 10^0
120.5 用十进制表示为:1.205 X 10^2

而计算机根本不认识十进制的数据,他只认识0和1。所以在计算机存储中,首先要将上面的数更改为二进制的科学计数法表示:

8.25 用二进制表示为:1000.01 可以表示为1.0001 X2^3
120.5 用二进制表示为:1110110.1 可以表示为1.1101101 X2^6

任何一个数的科学计数法表示都为1. xxx * 2n ,尾数部分就可以表示为xxxx,由于第一位都是1,所以将小数点前面的1省略。由此,23bit的尾数部分,可以表示的精度却变成了24bit,道理就是在这里。

对于指数部分,因为指数可正可负(占1位),所以8位的指数位能表示的指数范围就只能用7位,范围是:-127至128。所以指数部分的存储采用移位存储,存储的数据为元数据 加上 127

元数据 加上 127

“指数”从00000000开始(表示-127)至11111111(表示+128)
所以,10000000表示指数1 (127 + 1 = 128 --> 10000000 ) ;
指数为 3,则为 127 + 3 = 130,表示为 01111111 + 11 = 10000010 ;

8.25二进制存储.png
120.5二进制存储.png

二进制反推出浮点数:
如下内存数据:01000010111011010000000000000000,
将该数据分段:0 10000101 11011010000000000000000


image

计算出这样一组数据表示为:

1101101*10(133-127=6) =1.1101101 * 26 = 1110110.1=120.5

2.2和2.25的区别

单精度的 2.2 转换为双精度后,精确到小数点后13位之后变为了2.2000000476837
而单精度的 2.25 转换为双精度后,变为了2.2500000000000

2.25**** 的单精度存储方式表示为:0 10000001 00100000000000000000000

2.25**** 的双精度存储方式表示为:0 10000000 0010010000000000000000000000000000000000000000000000000

这样 2.25 在进行强制转换的时候,数值是不会变的。

**将十进制的小数转换为二进制的小数的方法是:****将小数*2****,取整数部分。**

   0.2×2=0.4,所以二进制小数第一位为0.4的整数部分0;

   0.4×2=0.8,第二位为0.8的整数部分0;

   0.8×2=1.6,第三位为1;

   0.6×2=1.2,第四位为1;

   0.2×2=0.4,第五位为0;

   ...... 这样永远也不可能乘到=1.0,得到的二进制是一个无限循环的排列 00110011001100110011...

对于单精度数据来说,尾数只能表示 24bit 的精度,所以2.2的 float 存储为:

2.2的二进制存储

但是这种存储方式,换算成十进制的值,却不会是2.2。

因为在十进制转换为二进制的时候可能会不准确(如:2.2),这样就导致了误差问题!

并且 double 类型的数据也存在同样的问题!

所以在浮点数表示中,都可能会不可避免的产生些许误差!

在单精度转换为双精度的时候,也会存在同样的误差问题。

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

推荐阅读更多精彩内容