4

比方说,我们已经声明了以下变量在C++中进行数学运算时,浮点错误是如何传播的?

float a = 1.2291;

float b = 3.99;

float变量具有精度6,这(如果我理解正确)表示数之间的差异,计算机实际存储并且你想要的实际数量将小于10^-6

这意味着ab都有一些误差小于10^-6

所以电脑a里面实际上可能1.229100000012123b可能是3.9900000191919

现在让我们假设你有下面的代码

float c = 0; 
for(int i = 0; i < 1000; i++) 
     c += a + b; 

我的问题是,

c的最终结果的精度误差小于10^-6作为w还是不是?

如果答案是否定的,我们如何才能真正知道这个精度错误,以及如果您应用任何类型的操作(如您希望的次数和顺序),究竟发生了什么?

+6

阅读它,它会回答你所有的问题和更多:http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html – avakar 2014-09-06 21:07:00

+0

该文章似乎翔实,我会研究它,因为浮动点错误让我很头痛,谢谢! – ksm001 2014-09-06 21:14:11

+0

您对精度的定义不正确。六位十进制数字的精度意味着该数字将精确到许多数字,而不会超过其数量级别。 – EJP 2014-09-06 21:31:44

回答

5

浮动变量具有精度6,其(如果我理解正确)表示的数量之间的差要将是计算机实际商店和实际数量小于10^-6

这意味着A和b两者有一些误差小于10^-6

的10 -6图是相对精度的粗略测量表示任意常数为浮点数时。不是所有的数字都会以绝对误差10 -6表示。例如,编号为8765432.1的预计可以近似地表示为单位。如果你至少有一点幸运,那么当你将其表示为float时,你会得到8765432。另一方面,1E-15f可以预计以至多约10的绝对误差来表示。

使电脑内部的实际上可能是1.229100000012123和B可能是3.9900000191919

没有,对不起,它的工作方式是不是你写的整个数字并添加六个零为可能出现的错误。可以通过从前导数字而不是从最后一位数字计算六个零来估计该误差。在这里,您可以预期1.22910012123或3.990000191919。

(其实你也会得到完全相同1.2290999889373779296875和3.9900000095367431640625。不要忘了,表示错误可能是负的,以及积极的,因为它是第一个数字。)

现在让我们说,你有下面的代码[...]

我的问题是,

c的最终结果具有精度误差小于10^-6还有没有?

号总绝对误差将所有的错误,表示为ab为每个使用过一千次的总和,再加上你做了2000和增补的错误。这是4000种不同的错误来源!它们中的许多将是相同的,它们中的一些将恰好相互补偿,但最终结果可能不是相对准确性,更像是相对准确性(建议不计算)。

3

这是一个非常好的问题,一个已经被许多权威人士解决了几十年的问题,本身就是一个计算机科学学科(for example)。从What Every Computer Scientist Should Know About Floating-Point Arithmetic

浮点运算被认为是受到很多人的一个深奥的主题。这是相当令人惊讶的,因为浮点在计算机系统中无处不在。几乎每种语言都有一个浮点数据类型;从PC到超级计算机的计算机都有浮点加速器;大多数编译器会被要求不时地编译浮点算法;几乎每个操作系统都必须响应浮点异常,例如溢出。本文提供了一个关于浮点的方面的教程,这些方面对计算机系统的设计者有直接的影响。它以浮点表示的背景和四舍五入错误开头,继续讨论IEEE浮点标准,并结合大量计算机构建者如何更好地支持浮点的例子。

(重点煤矿)

+1

“每个计算机科学家应该知道的浮点算术”几乎都不是关于数值分析,这正如你正确地指出一门学科本身,并且将任何计算机浮点标准早于3000年,如果维基百科是相信的:http://en.wikipedia.org/wiki/Numerical_analysis – 2014-09-06 23:25:57

+0

你提供的另一个参考文献,http://www.csee.wvu.edu/swarch/SARATool/docs/Error_Propagation_in_SW_Arch_Final.pdf,是关于在**分布式系统中传播**失败**。除了在标题中有“错误”和“传播”两个字之外,与手头主题没有任何关系。 – 2014-09-06 23:48:19

-2

简短的回答是,你可以很容易地确定浮点运算的长链的精度。

"c += a + b"这样的操作的精度不仅取决于浮点实现(现在几乎总是IEEE)的原始精度,而且取决于a,b和c的实际值。

继,编译器可以选择,以优化以不同的方式,这会导致意外的问题的代码,例如将其转化为"c+=a; c+=b;"或简单地执行环路"tmp = a*1000; tmp += b*1000; c += tmp;"或一些其它变型中,编译器会决定导致更快的执行时间但结果相同。

底线是仅通过检查源代码就无法分析精度。

由于这个原因,许多只是使用更高精度的实现,如double或long-double,然后检查精度问题是否出于所有实际目的。

如果这还不够,那么后退总是以整数实现所有逻辑并避免浮动。

+5

像你所描述的转换(['a +(b + c) - >(a + b)+ c'](http://gcc.gnu.org/wiki/FloatingPointMath#Transformations))只允许if你使用快速浮点优化进行编译,也就是说,当你在正常的浮点模式下编译时,不允许编译器按照浮点运算的顺序进行调整(对于GCC和MSVC来说,这是真的) – BeyelerStudios 2014-09-06 21:51:01

+4

你的“后退”选项几乎总是使所有的东西变得更糟。 – tmyklebu 2014-09-06 22:30:39

+0

在非常具体的情况下,定点算术可能是正确的选择。金钱是一个体面的例子 - 你有一个不可分割的'原子',比如说便士,它比标准单位少,但*不*按2的幂。在Bash中快速:'echo $((0.10))' - > 0.10000000000000001。定点解决这个问题 - 仅以便士来处理。一般来说,您正在与IEEE及其所有边缘案例竞争,以实现同样的错误。 (FYI,同样的方法:$((0.1 + $((0.2-0.2)))) - > 0.10 ... 1,'$(($((0.1 + 0.2)) - 0.2))' - > 0.10 ... 3。)[关于快速浮动](https://stackoverflow.com/a/16069874/1043529) – 2017-10-27 12:35:53