(第一个)问题
这是<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 值类型转换为ints,最终将返回一个未定义的int 值。
“修复”和第二个问题
要解决此问题,您所要做的就是提供一个正确键入的 init 值(即,提供一个 double 作为 init 参数)。只需0.0 即可:
std::inner_product(x.begin(), x.end(), y.begin(), 0.0);
现在,当您使用该修复程序编译并运行程序时,它仍然会输出不正确的结果:0
这是因为当inner_product 函数累积值时,它使用标准的double 加法来完成。因此,您需要遵守标准 double 不精确度、which has a machine epsilon 的 2^(-52) — 2.22E-16 或小数点后十六位的不精确度 — 这意味着对于数字 1E20,(1E20 + x ) = 1E20 对于所有 x
为了说明这一点,让我们在python解释器中添加1E20 + 23000(提醒python使用IEEE-754 floating point arithmetic,等于标准C++编译器中double的精度):
>>> 1e20 + 23000
1.0000000000000002e+20
所以你看到任何少于两万的东西都被忽略/“吸收”了。
由于您的其他数字小于 22204.46,1e20 只会“吸收”它们,直到它被添加到 -1E20,然后它将“取消”并返回 0。
(简单的)修复
解决第二个问题的最简单方法是使用long double 而不是double。这种更精确的双精度类型的机器 epsilon 为 2^(-63) — 1.08E-19 或大约十九位小数 — 这意味着,对于您的输入 1E20,不精度将等于 2^(-63) *1E20,或大约 10.84。运行程序,输出将是-4000,与预期的答案相当接近。 但这可能不是您的教授所期望的,因为他特别要求输出准确 -4000.4。
注意:显然,您可以选择另一种更精确的数字类型,但您的教授可能希望您使用double,所以我不会详细说明。
编辑: 正如 cmets 中提到的 @phuclv,some compilers 不会将 long double 实现为 80 位浮点值,而是可能具有与 @ 相同的精度987654356@(64 位)。因此,您可能必须寻找提供适当 80 位精度 long doubles 甚至 128-bit IEEE-754 quadruple-precision 浮点类型的库。虽然这肯定不会被认为是“容易的”。
(大部分正确的)修复
嗯,你不能无限精确,因为double 类型有 epsilon = 2^(-52),但你可以更聪明地加法,而不只是将大值添加到小值(请记住:由于double 浮点运算中的不精确性,大值会“吸收”小值)。基本上,您应该计算一个具有成对乘法的值的数组,然后对其进行排序(基于绝对值),然后使用std::accumulate 将值相加:
#include <iostream>
#include <numeric>
#include <vector>
#include <functional>
//Mind the use of these two new STL libraries
#include <algorithm> //std::sort and std::transform
#include <cmath> //abs()
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};
//The vector with the pairwise products
std::vector<double> products(x.size());
//Do element-wise multiplication
//C code: products[i] += x[i] * y[i];
std::transform(x.begin(), x.end(), y.begin(), products.begin(), std::multiplies<double>());
//Sort the array based on absolute-value
auto sort_abs = [] (double a, double b) { return abs(a) < abs(b); };
std::sort(products.begin(), products.end(), sort_abs);
//Add the values of the products(note the init=0.0)
double result = std::accumulate(products.begin(), products.end(), 0.0);
std::cout << result << std::endl;
return 0;
}
有了这个新代码,结果如预期:-4000.4
Tough 它显然有它的局限性。例如,如果输入是向量 v1 = {100.0, 1E20} 和 v2 = {10.0, 1.0},结果应该返回 100000000000000001000,显然只会返回 1E20。