Я создаю модульные тесты для функции, которая округляет «рациональные» числа, хранящиеся в виде строк.Текущая реализация округления преобразует строки в тип с плавающей запятой:
#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();
}
Хотя это решение решает проблему простым способом (против анализа строк вручную или создания числа Rational)класс), я нахожу это излишним для такой простой проблемы.
Чтобы найти способы решения этой проблемы, я заглянул в реализации некоторых калькуляторов.Я посмотрел на исходный код gnome-calculator и обнаружил, что он использует GNU MPFR.Затем я посмотрел на реализацию SpeedCrunch и обнаружил, что он повторно использует тот же код, что и bc, который использует рациональный тип (числитель, знаменатель).
Я что-то пропускаю?