【发布时间】:2018-11-07 12:58:38
【问题描述】:
我有一个简单的函数模板来计算容器的平均值:
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<>s 中添加了尝试将计算强制为所需的类型<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 本身的不当使用。我不确定它是否来自编译器警告,该警告与转换可能导致数据丢失有关,或者我是否将其转换错误,或者是否与我一般如何实现此功能有关。在提供链接之前,我已经接受了在此页面上找到的答案。我将保留此 Q/A 以供将来参考和读者!除此之外,我确实很欣赏提供的链接,因为它确实有助于了解错误在我的代码中的位置、错误是什么以及导致它的原因,以及除了此页面上接受的答案之外如何正确修复它。
【问题讨论】:
-
它应该返回 5.12,而不是 6.4。
-
@jwimberley 你是对的;我必须将这些值添加到 windows calc 错误...它是 5.12 而不是 6.4。但该函数仍然产生不正确的值。
-
使用不那么随意的测试用例——从容易判断错误的测试用例开始。
{1.5, 1.5}足以暴露错误,如果您没有不必要的单元素输入特殊情况,{1.5}就足够了。 -
@FrancisCugler 我知道它的用途。除非您知道绝大多数平均将是单元素容器,并且您已经测量了性能并确定它很重要,否则这是一个毫无意义的过早优化,只会增加复杂性。
-
您的问题因重复而被关闭这一事实不是判断性的。关闭它的条件是“这里已经回答了这个问题”。确实,您无法猜测要寻找什么才能找到此答案。你问这个问题做得很好(这是一个很好的问题 IMO)。不过,在链接的副本中有您的问题的答案。祝你有美好的一天。