为什么这个函数会产生不正确的值?
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
才能按您希望的方式工作。
我有一个简单的函数模板来计算容器的平均值:
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
才能按您希望的方式工作。