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;
单精度或双精度在存储中,都分为三个部分:
单精度和双精度的存储方式.png符号位 (Sign):0代表正数,1代表为负数;
指数位 (Exponent):用于存储科学计数法中的指数数据;
尾数部分 (Mantissa):采用移位存储尾数部分;
R32.24 和 R64.53 的存储方式都是用科学计数法来存储数据的,比如:
8.25 用十进制表示为:8.25 X
120.5 用十进制表示为:1.205 X
而计算机根本不认识十进制的数据,他只认识0和1。所以在计算机存储中,首先要将上面的数更改为二进制的科学计数法表示:
8.25 用二进制表示为:1000.01 可以表示为1.0001 X
120.5 用二进制表示为:1110110.1 可以表示为1.1101101 X
任何一个数的科学计数法表示都为1. xxx * 2n ,尾数部分就可以表示为xxxx,由于第一位都是1,所以将小数点前面的1省略。由此,23bit的尾数部分,可以表示的精度却变成了24bit,道理就是在这里。
对于指数部分,因为指数可正可负(占1位),所以8位的指数位能表示的指数范围就只能用7位,范围是:-127至128。所以指数部分的存储采用移位存储,存储的数据为元数据 加上 127
。
元数据 加上 127
:
8.25二进制存储.png 120.5二进制存储.png“指数”从00000000开始(表示-127)至11111111(表示+128)
所以,10000000表示指数1 (127 + 1 = 128 --> 10000000 ) ;
指数为 3,则为 127 + 3 = 130,表示为 01111111 + 11 = 10000010 ;
二进制反推出浮点数:
如下内存数据: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 类型的数据也存在同样的问题!
所以在浮点数表示中,都可能会不可避免的产生些许误差!
在单精度转换为双精度的时候,也会存在同样的误差问题。
网友评论