美文网首页iOS 的那些事儿
iOS开发浮点数计算精度问题

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

作者: Pandakingli | 来源:发表于2019-02-17 19:39 被阅读0次

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 类型的数据也存在同样的问题!

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

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

相关文章

  • iOS 浮点数的精确计算和四舍五入问题

    iOS开发中,使用浮点数(float,double)类型运算需要注意计算精度的问题。即使只是两位小数,也会出现误差...

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

    1、浮点数运算带来的问题 在日常工作中涉及到浮点数(float、double)的运算 2、浮点数运算精度的解决方案...

  • js浮点运算

    前言 在项目开发过程中发现浮点数的运算会出现精度问题,尤其是乘除运算。产生浮点数计算精度不准确的原因: 在计算机角...

  • iOS中的高精度数值计算

    前言 在iOS开发中,和价格计算相关的,需要注意计算精度的问题,使用float、double来计算价格数值会出现精...

  • js精度问题

    关于js浮点数计算精度不准确问题的解决办法 今天在计算商品价格的时候再次遇到js浮点数计算出现误差的问题,以前就一...

  • 浮点数计算精度问题

    产生的原因 计算机在进行运算时是将数值转为二进制后计算,我们可以通过num.toString(2)这个方法查看转为...

  • NSDecimalNumber

    在iOS开发中,进场遇到货币价格相关的计算,这是我们需要注意计算精度的问题。使用float类型运算,经常出现误差,...

  • iOS中NSDecimalNumber使用

    在iOS开发中,经常遇到和货币价格计算相关的,这时就需要注意计算精度的问题。使用float类型运算,经常出现误差。...

  • Objective-C货币的精确计算

    问题来源 在iOS开发中,和货币价格计算相关的,是需要注意计算精度的问题的。即使只是两位小数,也会出现误差。使用f...

  • Java浮点数计算精度问题总结

    Java浮点数计算精度问题总结 首先看看下面几个简单的加法计算的输出结果:System.out.println(0...

网友评论

    本文标题:iOS开发浮点数计算精度问题

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