C思考:浮动与整数和浮动表示

时间:2017-06-14 13:45:59

标签: c floating-point integer division integer-division

在C(以及许多其他语言)中使用整数时,在划分精度时必须注意。在分割之前,最好是乘以和添加东西(从而创建更大的中间结果,只要它不会溢出)。

但是漂浮物呢?这还有吗?或者它们是否以这样的方式表示,即将小数量的数量除以大数量的数量更好?

3 个答案:

答案 0 :(得分:3)

浮点数/双打和类似的浮点工作的表示,旨在保留有效数字的数量(又名"精度"),而不是固定数量的小数位,例如在定点中发生,或整数工作。

最好避免组合数量,这可能会导致指数的隐式下溢或溢出,即浮点数范围的限制。

因此,应尽量避免和重新安排大量不同幅度(明确地,或由于具有相反符号)的数量的相加/减少,以避免这种众所周知的路径丢失精度。

示例:重构/重新排序

更好
small + big + small + big + small * big

作为

(small+small+small) + big + big

因为小人物可能对大人物没有任何影响,因此他们的贡献可能会消失。

如果有任何"噪音"或者在任何数量的低位中不精确,了解有效位的丢失是如何通过计算传播的。

答案 1 :(得分:2)

用整数:
只要没有溢出,+,-,*总是准确的 通过除法,结果被截断,通常不等于数学答案 在ia,ib,icia*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的值,您将得到正确的结果结果

因此在处理浮点时没有简单的“在分割前总是添加”或“在添加之前总是分开”。作为一般规则,将操作数保持在相似的幅度通常是个好主意。这就是第三个例子的作用。