为什么这个函数会产生不正确的值?

Why is this function producing incorrect values?

我有一个简单的函数模板来计算容器的平均值:

template<typename T>
T array_average( std::vector<T>& values ) {
    if( std::is_arithmetic<T>::value ) {
        if( !values.empty() ) {
            if( values.size() == 1 ) {
                return values[0];
            } else { 
                return (static_cast<T>( std::accumulate( values.begin(), values.end(), 0 )  ) / static_cast<T>( values.size() ) );
            }
        } else {
            throw std::runtime_error( "Can not take average of an empty container" ); 
        }
    } else {
        throw std::runtime_error( "T is not of an arithmetic type" );
    }
}

我在上面的 static_cast<> 中添加了尝试强制计算到所需的类型 <T>

当我在 main 中使用 uint64_t

调用此函数时
std::vector<uint64_t> values{ 1,2,3,4,5,6,7,8,9,10,11,12 };
std::cout << array_average( values ) << '\n';

这段代码确实会产生 MSVC 的编译器警告 C4244 可能由于转换而丢失数据,但它运行正常,这给了我预期的结果,并将 6 打印到控制台。这是正确的,因为实际值为 6.5 但由于整数除法中的截断 6 是正确的。

现在如果我用上面的函数代替:

std::vector<double> values { 2.0, 3.5, 4.5, 6.7, 8.9 };
std::cout << array_average( values2 ) << '\n';

这应该给我 5.12 结果,但它显示的是 4.6。这也给了我与上面相同的编译器警告,但它运行时没有运行时错误(执行中断)但给了我不正确的结果。

我不确定我的函数中的错误在哪里。我不知道这是不是因为编译器警告,或者这是我设计函数本身的方式。


-编辑-

一位用户建议这可能与此重复 Q/A 我无法反驳它是否回答了我的问题。在问这个问题的时候;我不知道这个错误是来自 std::accumulate 本身的不当使用。我不确定它是否来自与可能因转换而丢失数据有关的编译器警告,或者我是否将其强制转换为错误,或者它是否与我实现此功能的方式有关。在提供 link 之前,我已经接受了在此页面上找到的答案。我将保留此 Q/A 以供将来参考和读者参考!除此之外,我非常感谢所提供的 link,因为它确实有助于了解错误在我的代码中的位置、错误是什么以及导致错误的原因,以及除了接受的答案之外如何正确修复它在此页面上。

你的static_cast放错地方了。您正在转换累加的 result,但让累加 运行 成为初始项的类型(此处 0,即 int).所以改为这样做:

return std::accumulate( values.begin(), values.end(), static_cast<T>(0) ) / static_cast<T>( values.size() );

(注意4.6确实是static_cast<double>(2 + 3 + 4 + 6 + 8) / 5.0的结果).


与问题核心无关的评论:

  • 该函数应该采用 const std::vector<T>&,因为它不会修改 values
  • 如果您使用对 std::accumulate 无效的 T 调用该函数(例如,不是算术),您将收到编译时错误。最上面的 if 必须是 if constexpr 才能按您希望的方式工作。