第一个问题
这是在<numeric>
中用于内积的函数模板:
template <class InputIterator1, class InputIterator2, class T>
T inner_product (InputIterator1 first1, InputIterator1 last1,
InputIterator2 first2, T init);
注意,定义输出类型T
的是init
参数。因此,给出你的输入:
std::inner_product(x.begin(), x.end(), y.begin(), 0);
init = 0
,因此类型T
为int
。所以,当算法运行时,它将把double
值强制转换为int
值,最终将返回一个未定义的int
值。
修复方法和第二个问题
要解决这个问题,您只需给出正确类型的init
值(即,将double
作为init
参数)。只需使用0.0
即可:
std::inner_product(x.begin(), x.end(), y.begin(), 0.0);
现在,当你对程序进行这个修正、编译并运行时,
它仍然输出错误的结果:
0
。
这是因为当
inner_product
函数累加数值时,使用的是标准的
double
加法。 因此,你受到标准的
double
不精确性的影响,其
机器ε是2^(-52) - 2.22E-16左右,在小数点后第十六位有误差,这意味着对于数字1E20,(1E20+x)=1E20对于所有的x<2^(-52)*1E20 ≈ 22204.46。
进一步解释这个问题:让我们在Python中求出
1E20+23000
(提醒:Python使用
IEEE-754浮点运算,与标准C++编译器中的
double
精度相同)。
>>> 1e20 + 23000
1.0000000000000002e+20
所以你可以看到,小于二万的任何东西都被忽略或"吸收"在这个加法中。
由于其他数字都小于22204.46,1e20将只是"吸收"它们,直到加到-1E20,然后将它们"取消"掉并返回0
。
(简单)修复方法
解决第二个问题最简单的方法是使用long double
而不是double
。这种更精确的双精度类型具有2^(-63)-1.08E-19或约十九个小数位的机器epsilon,这意味着对于您的输入1E20,不精确度将等于2^(-63)*1E20,即约10.84。运行程序,输出将为-4000
,非常接近预期答案。但这可能不是您的教授期望的结果,因为他特别要求输出精确地-4000.4
。
注意:显然,您可以选择另一种更精确的数字类型,但您的教授可能期望您使用double
,因此我不会详细介绍它。
编辑:如@phuclv在评论中提到的,
一些编译器没有将long double
实现为80位浮点值,而是可能具有与double
(64位)相同的精度。因此,您可能需要寻找提供正确的80位精度long double
或甚至128位IEEE-754四倍精度浮点类型的库。尽管这绝对不会被认为是"简单"的。
(大部分正确的)修复方法
嗯,你不能无限精确,因为double
类型的epsilon = 2^(-52),但你可以在加法中更聪明地处理,而不是直接将大值加到小值上(记住:由于double
浮点运算的不精确性,大值"吸收"小值)。基本上,您应该计算一个具有值的成对乘法的数组,然后将其排序(基于绝对值),然后使用std::accumulate
添加这些值:
#include <iostream>
#include <numeric>
#include <vector>
#include <functional>
#include <algorithm>
#include <cmath>
int main(){
std::vector<double> x{1.0e20, -1.0e3, 0.1, 1.0e20};
std::vector<double> y{1.0, 4.0, -4.0, -1.0};
std::vector<double> products(x.size());
std::transform(x.begin(), x.end(), y.begin(), products.begin(), std::multiplies<double>());
auto sort_abs = [] (double a, double b) { return abs(a) < abs(b); };
std::sort(products.begin(), products.end(), sort_abs);
double result = std::accumulate(products.begin(), products.end(), 0.0);
std::cout << result << std::endl;
return 0;
}
通过这段新代码,结果如预期所示:-4000.4
但它显然有其限制。例如,如果输入的向量是 v1 = {100.0, 1E20} 和 v2 = {10.0, 1.0},应该返回 100000000000000001000
作为结果,但显然只会返回1E20。
std::inner_product(x.begin(), x.end(), y.begin(), 0.0);
。请注意,最后一个参数使用双精度浮点型字面量0.0
而不是整型字面量0
。 - jonidouble N; //length of both vectors
length should be an integer, not double. The "standard" length type issize_t
- phuclv