浮点数float累加误差解决方式总结

首先是float累加产生误差的原因,该部分转自:http://blog.csdn.net/zhrh0096/article/details/38589067

1.  浮点数IEEE 754表示方法

要搞清楚float累加为什么会产生误差,必须先大致理解float在机器里怎么存储的,具体的表示参考[1] 和 [2], 这里只介绍一下组成

由上图可知(摘在[2]), 浮点数由: 符号位 + 指数位 + 尾数部分, 三部分组成。由于机器中都是由二进制存储的,那么一个10进制的小数如何表示成二进制。例如: 8.25转成二进制为1000.01, 这是因为 1000.01 = 1*2^3 + 0*2^2 + 0*2^1 + 0*2^0 + 0*2^-1 + 2*2^-2 = 1000.01.

(2)float的有效位数是6-7位,这是为什么呢?因为位数部分只有23位,所以最小的精度为1*2^-23 在10^-6和10^-7之间,接近10^-7,[3]中也有解释

那么为什么float累加会产生误差呢,主要原因在于两个浮点数累加的过程。

2. 两个浮点数相加的过程

两浮点数X,Y进行加减运算时,必须按以下几步执行(可参考 [4] 中插图):
(1)对阶,使两数的小数点位置对齐,小的阶码向大的阶码看齐。
(2)尾数求和,将对阶后的两尾数按定点加减运算规则求和(差)。
(3)规格化,为增加有效数字的位数,提高运算精度,必须将求和(差)后的尾数规格化。
(4)舍入,为提高精度,要考虑尾数右移时丢失的数值位。
(5)判断结果,即判断结果是否溢出。

关键就在与对阶这一步骤,由于float的有效位数只有7位有效数字,如果一个大数和一个小数相加时,会产生很大的误差,因为尾数得截掉好多位。例如:

123 + 0.00023456 = 1.23*10^2 + 0.000002 * 10^2 = 123.0002

那么此时就会产生0.00003456的误差,如果累加多次,则误差就会进一步加大。

解决方式有几种,但都不是最佳方式,参考:http://bbs.csdn.net/topics/390549664

3.解决方法

方法一

Kahan summation算法

https://en.wikipedia.org/wiki/Kahan_summation_algorithm

function KahanSum(input)
    var sum = 0.0
    var c = 0.0                 // A running compensation for lost low-order bits.
    for i = 1 to input.length do
        var y = input[i] - c    // So far, so good: c is zero.
        var t = sum + y         // Alas, sum is big, y small, so low-order digits of y are lost.
        c = (t - sum) - y       // (t - sum) cancels the high-order part of y; subtracting y recovers negative (low part of y)
        sum = t                 // Algebraically, c should always be zero. Beware overly-aggressive optimizing compilers!
    next i                      // Next time around, the lost low part will be added to y in a fresh attempt.
    return sum

伪代码如上

解决方法就是把多余的误差部分算出来(c),再在下一次循环减去这个误差

方法二

int main()
{
float f = 0.1;
float sum = 0;
sum+=add(f,4000000);
cout<<sum<<endl;
return 0;
}

float add(float f,int count)
{
    if(count==1)
    return f;
    else
        return add(f,count/2)+add(f,count-count/2);
}

二分法递归计算加法,这样会没有误差,但是函数调用消耗大(尤其是多次)

方法三

使用double,精度更高,但是本来是没有必要用这么高精度的

方法四

ieee浮点数,为了规格化,精度每超过2的整数次幂,精度要下降一位,
你的f是0.1,float位数是23,当sum足够大的时候,会出现 sum+f==sum 的情况,这个是ieee标准,
和C++没关系,事实上编译器应该已经做了浮点精度调整了,你这结果误差算小的了.
避免这种误差的方法就是浮点数,永远不要让一个很大的数去加上一个很小的数.不知你这段代码的目的是

什么,但如果你改成这样,误差会小很多:

float f = 0.1;
float sum = 0;
for( i=0; i<100; i++)
{
int sumEachBig=0;
for(....k<400....)
{
int sumEachSmall=0;
for(....j<100.....)
    sumEachSmall += f;

sumEachBig+=sumEachSmall;

}

sum += sumEachBig;

}

来自manzi11的回答。多次用多次循环,小循环的计算结果加上大循环的运算结果

by wolf96 2017/7/10

时间: 2024-11-08 12:21:58

浮点数float累加误差解决方式总结的相关文章

浮点数float累加误差分析与解决

1.  浮点数IEEE 754表示方法 要搞清楚float累加为什么会产生误差,必须先大致理解float在机器里怎么存储的,具体的表示参考[1] 和 [2], 这里只介绍一下组成 由上图可知(摘在[2]), 浮点数由: 符号位 + 指数位 + 尾数部分, 三部分组成.由于机器中都是由二进制存储的,那么一个10进制的小数如何表示成二进制.例如: 8.25转成二进制为1000.01, 这是因为 1000.01 = 1*2^3 + 0*2^2 + 0*2^1 + 0*2^0 + 0*2^-1 + 2*

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

浮点数在计算机中存储方式 C语言和C#语言中,对于浮点类型的数据采用单精度类型(float)和双精度类型(double)来存储,float数据占用32bit,double数据占用64bit,我们在声明一个变量float f= 2.25f的时候,是如何分配内存的呢?如果胡乱分配,那世界岂不是乱套了么,其实不论是float还是double在存储方式上都是遵从IEEE的规范的, float遵从的是IEEE R32.24 ,而double 遵从的是R64.53. 无论是单精度还是双精度在存储中都分为三个

常见浏览器兼容性问题与解决方式

所谓的浏览器兼容性问题,是指由于不同的浏览器对同一段代码有不同的解析,造成页面显示效果不统一的情况.在大多数情况下,我们的需求是,不管用户用什么浏览器来查看我们的站点或者登陆我们的系统,都应该是统一的显示效果.所以浏览器的兼容性问题是前端开发者常常会碰到和必需要解决的问题. 在学习浏览器兼容性之前,我想把前端开发者划分为两类: 第一类是精确依照设计图开发的前端开发者,能够说是精确到1px的,他们非常easy就会发现设计图的不足,而且在非常少的情况下会碰到浏览器的兼容性问题,而这些问题往往都死浏览

mixare的measureText方法在频繁调用时抛出“referencetable overflow max 1024”的解决方式

这几天在搞基于位置的AR应用,採用了github上两款开源项目: mixare android-argument-reality-framework 这两个项目实现机制大致同样.我选取的是android-argument-reality-framework.原因是我觉得他的代码结构要清晰非常多(纯属个人意见). 这两个项目的demo在执行时都会crash,通过查看控制台,能够看到例如以下信息: 07-31 14:35:38.685: W/dalvikvm(13686): ReferenceTab

精通CSS:高级Web标准解决方式(第2版)

精通CSS:高级Web标准解决方式(第2版) 跳转至: 导航. 搜索 层叠重要度:(也就是说.用户!important能够覆盖inline style) !important.用户>作者.最后是浏览器/用户代理 规则特殊性(次序)的计算:a,b,c,d 假设同样.后定义的优先 假设是inline style,a=1 b=ID选择器(#id)的个数 c=类.伪类.属性选择器的个数 d=类型.伪元素选择器的个数 => 大站点复杂内容情况下怎样管理规则次序??? @import比link慢? p41

mysql中采用concat来拼接中文字符乱码解决方式(转)

mysql中采用concat来拼接中文字符乱码解决方式 - fuxuejun的专栏 - 博客频道 - CSDN.NET http://blog.csdn.net/fuxuejun/article/details/6284725 mysql concat乱码问题解决 concat(str1,str2) 当concat结果集出现乱码时,大都是由于连接的字段类型不同导致,如concat中的字段参数一个是varchar类型,一个是int类型或doule类型,就会出现乱码. 解决方法:利用mysql的字符

SQL安装过程中“针对SQL Server 注册表的一致性验证“出错解决方式

1.打开注册表,查找到[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\0804],分别打开Counter 和Help 2.打开Counter,把滚动条拉倒最后,然后记下最大的那个值(不同计算机不一样的),记下后关闭窗口 3.同样的打开Help,滚动到最后记最大的那个值(你们的值不一定跟我一样的哦!!!),记下后关闭窗口 4,再重新定位到Perflib的节点上, 5.双击Last Counter然后在

MySQL安装过程中出现“APPLY security settings错误”的解决方式

***********************************************声明****************************************************** 原创作品,出自 "晓风残月xj" 博客,欢迎转载,转载时请务必注明出处(http://blog.csdn.net/xiaofengcanyuexj). 因为各种原因.可能存在诸多不足,欢迎斧正. *******************************************

hadoop一些常见报错的解决方式

Failed to set setXIncludeAware(true) for parser 遇到此问题通常是jar包冲突的问题. 一种情况是我们向java的lib文件夹加入我们自己的jar包导致hadoop引用jar包的冲突.解决方式就是删除我们自己向系统加入的jar包,又一次配置.将自己的jar包或者外部jar放入系统文件夹会在编译程序时带来方便,可是这不是一种好习惯,我们应该通过改动CLASSPATH的方式指定jar包路径. Cannot lock storage /tmp/hadoop