namespace util {
+nonstd::expected<double, std::string>
+parse_double(const std::string& value)
+{
+ size_t end;
+ double result;
+ bool failed = false;
+ try {
+ result = std::stod(value, &end);
+ } catch (const std::exception&) {
+ failed = true;
+ }
+
+ if (failed || end != value.size()) {
+ return nonstd::make_unexpected(
+ FMT("invalid floating point: \"{}\"", value));
+ } else {
+ return result;
+ }
+}
+
nonstd::expected<int64_t, std::string>
parse_signed(const std::string& value,
const nonstd::optional<int64_t> min_value,
std::string
join(const T& begin, const T& end, const nonstd::string_view delimiter);
+// Parse a string into a double.
+//
+// Returns an error string if `value` cannot be parsed as a double.
+nonstd::expected<double, std::string> parse_double(const std::string& value);
+
// Parse a string into a signed integer.
//
// Returns an error string if `value` cannot be parsed as an int64_t or if the
}
}
+TEST_CASE("util::parse_double")
+{
+ CHECK(*util::parse_double("0") == doctest::Approx(0.0));
+ CHECK(*util::parse_double(".0") == doctest::Approx(0.0));
+ CHECK(*util::parse_double("0.") == doctest::Approx(0.0));
+ CHECK(*util::parse_double("0.0") == doctest::Approx(0.0));
+ CHECK(*util::parse_double("2.1") == doctest::Approx(2.1));
+ CHECK(*util::parse_double("-42.789") == doctest::Approx(-42.789));
+
+ CHECK(util::parse_double("").error() == "invalid floating point: \"\"");
+ CHECK(util::parse_double("x").error() == "invalid floating point: \"x\"");
+}
+
TEST_CASE("util::parse_signed")
{
CHECK(*util::parse_signed("0") == 0);