在计算总和时使用更大的精度并在算法结束时降低精度是一个好习惯吗?像
float average(const float* begin, const float* end)
{
double sum=0;
size_t N=end-begin;
while(begin!=end)
{
sum+=(double)(*begin);
++begin;
}
return (float)( sum/N); //Assume range is not empty
}
可能是因为积累中的错误较少。另一方面,在数据类型之间进行转换时可能会出错。
答案 0 :(得分:5)
这取决于你想要避免的事情,但可能不是。
如果你试图避免灾难性的取消(10^100 + 1 - 10^100
导致0而不是1),使用更广泛的FP类型会有所帮助,但不是很多。
如果这些数字在数量上更接近一致,但是你仍然担心随着总和的增长LSB会不断下降(例如1e-8 + 1e-8 + (1e8 copies) != 1
),那么更广泛的类型可以帮助,但同样,只有一点。
真正帮助的是更聪明的浮点求和方法。最简单的方法称为"成对求和",其中您将数字数组视为二叉树的叶子,并递归地对它们进行求和,直到您只剩下一个数字。对于您在那里进行的迭代求和,您还可以先对数字进行排序,这样可以减少错误。并且有更复杂,更精确的方法可用...谷歌"补偿总和"详情。
所有这些都是说,如果您怀疑舍入错误对您来说是个问题,double sum
会有所帮助,但可能还不够。
哦,关于"在数据类型之间进行转换时可能会出现问题":可能出错(特别是双舍入错误),但是与执行求和本身的错误相比,您可能从中看到的不精确度并不重要。
答案 1 :(得分:2)
不是良好做法的一件事是最终降低精度。
无论如何,你的代码除以零,因为当你进行除法时,开始==结束。
答案 2 :(得分:1)
我不会:做这种事情会进一步将您的实现与特定平台联系起来。在C中不能保证float
不如double
精确,并且最后的精确度降低不是好的做法,而且计算上也不是特别便宜。
我会让编译器完成它的工作。
虽然以浮点数添加数字,但最好先积累较小的数字。然后他们有更好的机会为总和作出贡献。有更先进的浮点求和方法;你也应该考虑它们。
答案 3 :(得分:0)
Sneftel提到了求和方法。以下是一组函数,它们使用2048 IEEE 64位双精度数组(由调用者传递)。 (假设无符号长long也是64位)。
/* clear array */
void clearsum(double asum[2048])
{
size_t i;
for(i = 0; i < 2048; i++)
asum[i] = 0.;
}
/* add a number into array */
void addtosum(double d, double asum[2048])
{
size_t i;
while(1){
/* i = exponent of d */
i = ((size_t)((*(unsigned long long *)&d)>>52))&0x7ff;
if(i == 0x7ff){ /* max exponent, could be overflow */
asum[i] += d;
return;
}
if(asum[i] == 0.){ /* if empty slot store d */
asum[i] = d;
return;
}
d += asum[i]; /* else add slot to d, clear slot */
asum[i] = 0.; /* and continue until empty slot */
}
}
/* return sum from array */
double returnsum(double asum[2048])
{
double sum = 0.;
size_t i;
for(i = 0; i < 2048; i++)
sum += asum[i];
return sum;
}