我正在为一个将存储为字符串的“有理”数字舍入的函数创建单元测试。当前的舍入实现将字符串转换为浮点类型:
#include <boost/lexical_cast.hpp>
#include <iomanip>
#include <limits>
#include <sstream>
template<typename T = double,
size_t PRECISION = std::numeric_limits<T>::digits10>
std::string Round(const std::string& number)
{
std::stringstream ss{};
ss << std::fixed << std::setprecision(PRECISION);
ss << boost::lexical_cast<T>(number);
return ss.str();
}
在我的一项测试中,我在我的机器上输入了数字 3.55,它表示为 3.5499999...。从 2 位小数舍入到 10 时一切顺利。但是,当我舍入到第一个小数时,不出所料,我得到 3.5 而不是 3.6。
避免此错误的简单方法是什么?
目前,我能找到的最佳解决方案是使用多精度类型:
#include <boost/multiprecision/cpp_dec_float.hpp>
#include <iomanip>
#include <sstream>
template<size_t PRECISION = 10>
std::string Round(const std::string& number)
{
using FixedPrecision =
boost::multiprecision::number<
boost::multiprecision::cpp_dec_float<PRECISION>>;
std::stringstream ss{};
ss << std::fixed << std::setprecision(PRECISION);
ss << FixedPrecision{number};
return ss.str();
}
虽然这个解决方案以一种直接的方式解决了这个问题(相对于手动解析字符串或创建一个有理数类),但我发现对于这样一个简单的问题来说它过分了。
为了找到解决这个问题的方法,我查看了一些计算器的实现。我查看了 gnome-calculator 的源代码,发现它使用了 GNU MPFR。然后我查看了 SpeedCrunch 的实现,发现它重用了与 bc 相同的代码,它采用了有理类型(分子、分母)。
我忽略了什么吗?