在C(以及许多其他语言)中使用整数时,在划分精度时必须注意。在分割之前,最好是乘以和添加东西(从而创建更大的中间结果,只要它不会溢出)。
但是漂浮物呢?这还有吗?或者它们是否以这样的方式表示,即将小数量的数量除以大数量的数量更好?
答案 0 :(得分:3)
浮点数/双打和类似的浮点工作的表示,旨在保留有效数字的数量(又名"精度"),而不是固定数量的小数位,例如在定点中发生,或整数工作。
最好避免组合数量,这可能会导致指数的隐式下溢或溢出,即浮点数范围的限制。
因此,应尽量避免和重新安排大量不同幅度(明确地,或由于具有相反符号)的数量的相加/减少,以避免这种众所周知的路径丢失精度。
示例:重构/重新排序
更好small + big + small + big + small * big
作为
(small+small+small) + big + big
因为小人物可能对大人物没有任何影响,因此他们的贡献可能会消失。
如果有任何"噪音"或者在任何数量的低位中不精确,了解有效位的丢失是如何通过计算传播的。
答案 1 :(得分:2)
用整数:
只要没有溢出,+,-,*
总是准确的
通过除法,结果被截断,通常不等于数学答案
在ia,ib,ic
与ia*ib/ic
分开之前乘以ia*(ib/ic)
更好,因为商是基于产品ia*ib
的更多位而不是ib
。
浮点数:
问题很微妙。同样,只要没有上溢/下溢,顺序或*,/
序列的影响小于整数。 FP */-
类似于添加/减去日志。典型结果在数学上正确答案的0.5 ULP范围内。
对于FP和+,-
,fa,fb,fc
的结果可能与数学正确的结果有显着差异,1)数值相差很大或2)减去几乎相等的值和误差先前的计算现在变得很重要。
考虑二次方程:
double d = sqrt(b*b - 4*a/c); // assume b*b - 4*a/c >= 0
double root1 = (-b + d)/(2*a);
double root2 = (-b - d)/(2*a);
对战
double d = sqrt(b*b - 4*a/c); // assume b*b - 4*a/c >= 0
double root1 = (b < 0) ? (-b + d)/(2*a) : (-b - d)/(2*a)
double root2 = c/(a*root1); // assume a*root1 != 0
当一个根接近0并且root2
接近|b|
时,第二个具有更好的d
精度结果。这是因为b,d
减法消除了许多重要位,使d
计算中的误差变得显着。
答案 2 :(得分:0)
(对于整数)在分割之前,总是更好地乘以和添加东西(因此创建一个更大的中间结果,只要它不会溢出)。
这仍然适用(浮动)吗?
一般来说,答案是否
很容易构建一个示例,其中在除法之前添加所有输入会给您带来巨大的舍入误差。
假设您要添加10000000000个值并将它们除以1000.进一步假设每个值为1.因此预期结果为10000000.
方法1 但是,如果在除法之前添加所有值,则会得到结果16777.216(对于32位浮点数)。正如你所看到的那样,它已经差不多了。
方法2 那么在将每个值加到结果之前将它除以1000会更好吗?如果你这样做,你将得到结果32768.0(对于32位浮点数)。你可以看到它也差不多了。
方法3 但是,如果您继续添加值,直到临时结果大于1000000,然后将临时结果除以1000并将该中间结果添加到最终结果并重复该结果,直到您添加了总计10000000000的值,您将得到正确的结果结果
因此在处理浮点时没有简单的“在分割前总是添加”或“在添加之前总是分开”。作为一般规则,将操作数保持在相似的幅度通常是个好主意。这就是第三个例子的作用。