浮点运算:浮点运算结果出现误差原因分析及解决方案

如下C#代码:

float a = 0.65f;

float b = 0.6f;

float c = a - b;

此时c为多少?

0.05?

此时c为0.0499999523!

为什么?

其根本原因是计算机所使用 2进制01代码无法准确表示某些带小数位十进制数据

下面我们来分析下:

我们知道将个十进制数值转换为 2进制数值需要通过下面计算思路方法:

1. 整数部分:连续用该整数除以2取余数然后商再除以2直到商等于0为止然后把得到各个余数按相反顺序排列简称"除2取余法"

2. 小数部分:十进制小数转换为 2进制小数采用"乘2取整顺序排列"法用2乘以十进制小数将得到整数部分取出再用2乘余下小数部分然后再将积整数部分取出如此进行直到积中小数部分为0或者达到所要求精度为止然后把取出整数部分按顺序排列起来即先取出整数部分作为 2进制小数高位后取出整数部分作为低位有效位简称"乘2取整法"

3. 含有小数十进制数转换成 2进制整数、小数部分分别进行转换然后相加

例如:将十进制数值25.75转换为 2进制数值步骤如下:

25(整数部分)

25/2=12......1

12/2=6.......0

6/2=3......0

3/2=1......1

1/2=0......1

(25) 10=(11001) 2

0.75(小数部分)

0.75*2=1.5......1

0.5*2=1......1

(0.75) 10=(0.11) 2

(25.75) 10=(11001) 2+(0.11) 2=(11001.11) 2

按照上述思路方法我们将0.65及0.6转换为 2进制代码:

(0.65)10 = (0.101001100110011001100110011001100110011......)2

(0.6) 10 = (0.10011001100110011001100110011001100110011......)2

后面省略号表示已经算不完了后面在无限重复 0011 这段 2进制数值

文章开始部分我们用float类型下面我们来看看float类型是否能存储上面转换出 2进制代码

目前计算机上存储浮点数值是按照IEEE(电气和电子工程师协会)754浮点存储格式标准来存储

IEEE单精度浮点格式共32位包含 3个构成字段:23位小数f8位偏置指数e1位符号s将这些字段连续存放在个32位字里并对其进行编码其中0:22位包含23位小数f; 23:30位包含8位指数e;第31位包含符号s如下图所示:
clip_image001

也就是说上面将0.65及0.5转换出 2进制代码我们只能存储23位即使数据类型为double也只能存储52位这样大家便能看出问题出现原因了

截取 2进制代码已无法正确表示0.65及0.5根据这个 2进制代码肯定无法正确得到结果0.05

 

如何解决这个问题?知道其根本原因后我们知道是无法从根本上解决这个问题但我们可以有些曲线救国思路方法下面列举几个:

1. 2进制数值可以准确表示整数(可以使用整数转换为 2进制思路方法验证下)所以可以将小数乘以10或100等变成整数然后做运算最后再通过除以10或100等获得结果;

2. 通过截取结果有效小数位数等来取得最好近似结果然后在做处理

3. 对于可以用有限长度 2进制数值表示十进制数值可以使用存储位数大于其长度数据类型

解决方案正在补充中……若各位有什么好思路方法也可以提出来!

以上解决方案需要按照使用实际情况来决定使用哪种思路方法

Tag标签: 浮点运算误差
Tags:  arm浮点运算 cpu浮点运算 什么是浮点运算 浮点运算

延伸阅读

最新评论

发表评论