我有两个近1000个大小的整数向量,我要做的是检查这两个向量的平方整数之和是否相同。所以我写下面的代码:
std::vector<int> array1;
std::vector<int> array2;
... // initialize array1 and array2, and in the experiment all elements
// in the two vectors are the same but the sequence of elements may be different.
// For example: array1={1001, 2002, 3003, ....}
// array2={2002, 3003, 1001, ....}
assert(array1.size() == array2.size());
float sum_array1 = 0;
float sum_array2 = 0;
for(int i=0; i<array1.size(); i++)
sum_array1 +=array1[i]*array1[i];
for(int i=0; i<array2.size(); i++)
sum_array2 +=array2[i]*array2[i];
我希望sum_array1
应该等于sum_array2
,但事实上在我的应用程序中,我发现它们sum_array1 = 1.2868639e+009
与sum_array2 = 1.2868655e+009
不同。我接下来要做的是将sum_array1
和sum_array2
的类型更改为double类型,如下面的代码所示:
double sum_array1 = 0;
double sum_array2 = 0;
for(int i=0; i<array1.size(); i++)
sum_array1 +=array1[i]*array1[i];
for(int i=0; i<array2.size(); i++)
sum_array2 +=array2[i]*array2[i];
此时间sum_array1
等于sum_array2
sum_array1=sum_array2=1286862225.0000000
。我的问题是它为什么会发生。谢谢。
答案 0 :(得分:4)
在两个循环中,您添加相同的数字,但顺序不同。一旦总和超过可以由float
精确表示的整数值,您将开始失去精度,并且总和可能会略有不同。
您尝试的实验:
float n = 0;
while (n != n + 1)
n = n + 1;
//Will this terminate? If so, what is n now?
如果你运行它,你会发现循环实际终止 - 这看起来完全违反直觉,但根据IEEE single-precision floating point算法的定义是正确的行为。 / p>
您可以尝试相同的实验,将float
替换为double
。您将目睹相同的奇怪行为,但这次循环将在n
更大时终止,因为IEEE double-precision floating point数字可以实现更精确的准确性。
答案 1 :(得分:4)
浮点值具有有限的大小,因此只能表示具有有限精度的实数值。当您需要比存储精度更高的精度时,这会导致舍入错误。
特别是,当将较小的数字(例如你正在求和的那些)添加到更大的数字(例如累加器)时,与较小的数字相比,精度损失可能非常大,从而产生重大错误;并且错误将根据订单而有所不同。
通常,float
具有24位精度,对应于大约7位小数。您的累加器需要10位小数(大约30位),因此您将遇到这种精度损失。通常,double
有53位(大约16位小数),因此您的结果可以准确表示。
64位整数可能是最佳选择,因为所有输入都是整数。使用整数可以避免精度损失,但如果输入太多或太大,则会带来溢出的危险。
如果不能使用足够宽的累加器,要最小化错误,可以对输入进行排序,以便首先累积最小值;或者您可以使用更复杂的方法,例如Kahan summation。
答案 2 :(得分:3)
浮点表示(通常为IEEE754)使用有限位来表示小数,因此operations with floating-point numbers result in precision loss。
通常,与常识相反,如果a == ((a+1)-1)
是浮点变量,则false
等比较会产生a
。
要比较两个浮点,必须使用一种“精确损失范围”。也就是说,如果一个数字与 precision-loss-range 之间的数字不同,您认为数字是相等的:
//Supposing we can overload operator== for floats
bool operator==( float lhs , float rhs)
{
float epsilon = std::numeric_limits<float>.epsilon();
return std::abs(lhs-rhs) < epsilon;
}
答案 3 :(得分:2)
double
有更多位,因此比float
拥有更多信息。当您向浮点数添加值时,它将最终在sum_array1与sum_array2的不同时间舍入信息。
根据输入值,当使用double作为float时(如果值足够大),最终可能会遇到相同的问题。
通过网络搜索“您需要了解的有关浮点数的所有信息”,可以很好地了解这些限制,以及如何最好地处理它们。