在C ++中进行数学运算时,浮点错误如何传播?
假设我们已经声明了以下变量
float a = 1.2291;
float b = 3.99;
float
变量有精度6,(如果我理解正确的话)意味着计算机实际存储的数量与你想要的实际数量之间的差异将小于10^-6
这意味着a
和b
都有一些小于10^-6
错误
所以在计算机内部实际上可能是1.229100000012123
而b
可能是3.9900000191919
现在让我们说你有以下代码
float c = 0; for(int i = 0; i < 1000; i++) c += a + b;
我的问题是,
c
的最终结果是否会产生小于10^-6
的精度误差?
如果答案是否定的,那么我们怎么能真正知道这个精确度错误以及如果你按照自己的意愿和任何顺序应用任何类型的操作会发生什么?
浮点变量有精度6,(如果我理解正确的话)意味着计算机实际存储的数量与你想要的实际数量之间的差异将小于10 ^ -6
这意味着a和b都有一些小于10 ^ -6的错误
10 -6数字是将任意常数表示为浮点数时相对精度的粗略度量。 并非所有数字都以10 -6的绝对误差表示。 例如,可以预期数字8765432.1大约表示该单位。 如果你至少有点幸运,那么当你把它表示为float
时,你会得到8765432。 另一方面,可以预期1E-15f
的绝对误差最多为约10 -21 。
所以在计算机内部实际上可能是1.229100000012123而b可能是3.9900000191919
不,对不起,它的工作方式不是你编写整个数字并为可能的错误添加六个零。 可以通过计算前导数字中的六个零而不是最后一个数字来估计误差。 在这里,你可以期待1.22910012123或3.990000191919。
(实际上你会得到正好1.2290999889373779296875和3.9900000095367431640625。不要忘记表示错误可以是负数也可以是正数,因为它是第一个数字。)
现在让我们说你有以下代码[…]
我的问题是,
c
的最终结果是否会产生小于10 ^ -6的精度误差?
不会。总绝对误差将是您使用它们的千次中每一次的a
和b
的所有表示错误的总和,加上您所做的2000次加法的错误。 这是4000种不同的错误来源! 其中许多都是相同的,其中一些会碰巧相互补偿,但最终结果可能不会达到10 -6相对准确度,更像是相对准确度为10 -5 (建议不计算)。
这是一个非常好的问题,这个问题已经被许多权威机构解决了几十年,并且本身就是一门计算机科学学科( 例如 )。 从每个计算机科学家应该知道的浮点运算 :
浮点算术被许多人认为是一个深奥的主题。 这是相当令人惊讶的,因为浮点在计算机系统中无处不在。 几乎每种语言都有浮点数据类型; 从PC到超级计算机的计算机都有浮点加速器; 大多数编译器都会被要求不时编译浮点算法; 几乎每个操作系统都必须响应溢出等浮点exception。 本文提供了一个关于浮点的那些方面的教程,这些方面对计算机系统的设计者有直接影响。 它首先介绍浮点表示和舍入错误 ,继续讨论IEEE浮点标准,最后总结了许多计算机构建器如何更好地支持浮点数的例子。
(强调我的)
简短的回答是,您无法轻松确定长链浮点运算的精度。
像"c += a + b"
这样的操作的精度不仅取决于浮点实现的原始精度(现在几乎总是IEEE),而且还取决于a,b和c的实际值。
此外,编译器可能选择以不同方式优化代码,这可能导致意外问题,例如将其转换为"c+=a; c+=b;"
或者简单地将循环作为"tmp = a*1000; tmp += b*1000; c += tmp;"
或者编译器将确定的一些其他变体导致更快的执行时间但是相同的结果。
最重要的是,仅通过检查源代码就无法进行精度分析。
出于这个原因,许多人只是使用更高精度的实现,如double或long-double,然后检查精度问题是否已经用于所有实际目的。
如果这还不够,那么回退总是以整数实现所有逻辑并避免浮点数。