Why two float type variables have different values
我有两个大小接近1000的整数向量,我要做的是检查这两个向量的平方和是否相同。所以我写了以下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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]; |
我希望
1 2 3 4 5 6 | 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]; |
号
此时
浮点值的大小是有限的,因此只能以有限的精度表示实值。当您需要比存储精度更高的精度时,这会导致舍入错误。
特别是,当把一个小数字(比如你要求和的那些)加到一个大得多的数字(比如你的累加器)中时,与小数字相比,精度损失可能相当大,会产生很大的错误;并且错误会因顺序不同而不同。
通常,
64位整数可能是这里最好的选择,因为所有的输入都是整数。使用整数可以避免精度损失,但如果输入太多或太大,则会有溢出的危险。
如果不能使用足够宽的累加器,可以对输入进行排序,以便首先累积最小值;或者可以使用更复杂的方法,如Kahan求和。
在这两个循环中,您添加的数字相同,但顺序不同。一旦总和超过了可以用
你可以尝试的实验:
1 2 3 4 | float n = 0; while (n != n + 1) n = n + 1; //Will this terminate? If so, what is n now? |
号
如果您运行这个,您会发现循环实际上终止了——这看起来完全是违反直觉的,但是根据IEEE单精度浮点运算的定义,这是正确的行为。
你可以尝试同样的实验,用
浮点表示(通常是IEEE754)使用有限位来表示小数,因此使用浮点数的操作会导致精度损失。
通常,与常识相反,如果
要比较两个浮点,必须使用一种"精度损失范围"。也就是说,如果一个数字与另一个数字的差小于该精度损失范围,则认为该数字相等:
1 2 3 4 5 6 7 | //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; } |
根据输入值的不同,当使用double作为float(如果值足够大)时,您可能会遇到相同的问题。
在网络上搜索"关于浮点数你需要知道的一切",可以让你很好地了解这些限制,以及如何最好地处理它们。