std::reduce 似乎将结果转换为整数

std::reduce seems to convert results to integers

本文关键字:转换 整数 结果 reduce std      更新时间:2023-10-16

我正在比较这两个函数:

double polynomials(const vector<double> & coeffs, double x) {
double sum = 0.0;
double factor = 1.0;
for (double coeff: coeffs) {
sum += coeff * factor;
factor *= x;
}
return sum;
}

double algorithm_polynomials(const vector<double> & coeffs, double x) {
return reduce(execution::seq, cbegin(coeffs), end(coeffs), 0, [factor=1.0, x](double sum, double coeff) mutable {
double curr_sum = sum + coeff * factor;
factor *= x;
return curr_sum;
});
}

对于整数系数和整数 x,这两个函数的结果是等效的。但对于实际值,结果会有所不同。第一个函数给出的是正确的,而第二个函数给出的总是整数(不一定接近实际答案(。

我的猜测是,每个中间值在求和之前都被转换为整数,但我不明白为什么会发生这种情况,或者我该如何解决它。

要运行此代码,请使用

#include <numeric>
#include <vector>
#include <algorithm>
#include <execution>
using namespace std;

需要带有 -std=C++17 标志的 GCC 9 或更高版本,并从 2019 年开始链接到 TBB。

编辑:谢谢大家指出我忽略的初始值类型。

我感到有点羞愧,因为没有看到它,以及使用不当的reduce函数(由于可变的lambda而无法并行化(。

为了救赎自己,我在这里添加一种方法来使其并行......

class counter: public std::iterator<
std::random_access_iterator_tag,   // iterator_category
size_t,                      // value_type
size_t,                      // difference_type
const size_t*,               // pointer
size_t                       // reference
>{
size_t num = 0;
public:
explicit counter(size_t _num) : num(_num) {}
counter& operator++() {num += 1; return *this;}
counter operator++(int) {counter retval = *this; ++(*this); return retval;}
bool operator==(counter other) const {return num == other.num;}
bool operator!=(counter other) const {return !(*this == other);}
counter& operator+=(size_t i) { num += i; return *this; }
counter& operator-=(size_t i) { num -= i; return *this; }
counter operator +(counter &other) const { return counter(num + other.num);}
counter operator -(counter &other) const { return counter(num - other.num); }
counter operator +(size_t i) const { return counter(num + i); }
counter operator -(size_t i) const {return counter(num - i); }
reference operator*() const {return num;}
};

double better_algorithm_polinomials(const vector<double> & coeffs, double x) {
//this has the advantage of being easily parallelized
return transform_reduce(execution::par, cbegin(coeffs), end(coeffs), counter(0), 0.0, plus{}, [x](double coeff, size_t index) { return coeff * pow<double>(x, index); });
}

您正在调用的std::reduce()的版本:

template<class ExecutionPolicy, class ForwardIt, class T, class BinaryOp>
T reduce(ExecutionPolicy&& policy,
ForwardIt first, ForwardIt last, T init, BinaryOp binary_op);

您可以清楚地看到返回值使用与init参数相同的数据类型,在您的情况下,该参数被推导出为int,这就是为什么结果是int.

若要使返回值改为double,只需在init参数中将文本0更改为0.0

return reduce(execution::seq, cbegin(coeffs), cend(coeffs), 0.0, ...);

如果您查看std::reduce()的声明,您会发现将init值作为输入的std::reduce()版本使用其类型作为返回类型。根据 cppreference.com,您将看到以下内容:

template<class InputIt, class T>
T reduce(InputIt first, InputIt last, T init);

或者,在您的情况下(感谢雷米·勒博注意到(:

template<class ExecutionPolicy, class ForwardIt, class T, class BinaryOp>
T reduce(ExecutionPolicy&& policy,
ForwardIt first, ForwardIt last, T init, BinaryOp binary_op);

因此,init值的类型决定了输出值的类型,并且您正在传递一个整数文本(即0(。尝试传递双精度文字(即0.0(。

std::accumulate()也有同样的陷阱。返回类型(以及累加器变量的类型(与init参数的类型相同。

由于0是一个int,结果也是一个int。请改用0.0