Skip to content

Commit fefc51c

Browse files
authored
Merge pull request #1272 from cppalliance/from_string
Add `sto*` family of `std::string` conversion functions
2 parents fe3bbd6 + 0d0bd12 commit fefc51c

File tree

4 files changed

+219
-0
lines changed

4 files changed

+219
-0
lines changed

doc/modules/ROOT/pages/strings.adoc

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,39 @@ They construct the value as though calling `from_chars` without a specified form
3838
If the input string is invalid these constructors will `throw std::runtime_error`.
3939
If you are using a no exceptions environment instead of throwing the constructor will return a Quiet NAN.
4040

41+
== Conversions from `std::string`
42+
43+
[source,c++]
44+
----
45+
#include <boost/decimal/string.hpp>
46+
47+
namespace boost {
48+
namespace decimal {
49+
50+
inline auto stod32(const std::string& str, std::size_t* idx = nullptr) -> decimal32_t;
51+
52+
inline auto stod32f(const std::string& str, std::size_t* idx = nullptr) -> decimal_fast32_t;
53+
54+
inline auto stod64(const std::string& str, std::size_t* idx = nullptr) -> decimal64_t;
55+
56+
inline auto stod64f(const std::string& str, std::size_t* idx = nullptr) -> decimal_fast64_t;
57+
58+
inline auto stod128(const std::string& str, std::size_t* idx = nullptr) -> decimal128_t;
59+
60+
inline auto stod128f(const std::string& str, std::size_t* idx = nullptr) -> decimal_fast128_t
61+
62+
} // namespace decimal
63+
} // namespace boost
64+
----
65+
66+
Attempts conversion of `str` to the decimal type specified as if with `from_chars(str, idx)` subject to:
67+
68+
. Overflow throws `std::out_of_range` or in a no exceptions environment returns `std::numeric_limits<DecimalType>::signaling_NaN()` with unset value of `idx`.
69+
70+
. If the string can not be converted into a decimal value throws `std::out_of_range` or in a no exceptions environment returns `std::numeric_limits<DecimalType>::signaling_NaN()` with unset value of `idx`.
71+
72+
The returned value `idx` is the number of characters.
73+
4174
== `to_string`
4275

4376
[source, c++]

include/boost/decimal/string.hpp

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,68 @@
1313
namespace boost {
1414
namespace decimal {
1515

16+
namespace detail {
17+
18+
template <typename DecimalType>
19+
auto from_string_impl(const std::string& str, std::size_t* idx)
20+
BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, DecimalType)
21+
{
22+
DecimalType val;
23+
const auto r {from_chars(str, val)};
24+
25+
if (r.ec == std::errc::result_out_of_range || val == std::numeric_limits<DecimalType>::infinity())
26+
{
27+
val = std::numeric_limits<DecimalType>::signaling_NaN();
28+
BOOST_DECIMAL_THROW_EXCEPTION(std::out_of_range("Conversion is outside the range of the type"));
29+
}
30+
else if (r.ec != std::errc{})
31+
{
32+
val = std::numeric_limits<DecimalType>::signaling_NaN();
33+
BOOST_DECIMAL_THROW_EXCEPTION(std::invalid_argument("Conversion could not be performed"));
34+
}
35+
else
36+
{
37+
if (idx != nullptr)
38+
{
39+
*idx = static_cast<std::size_t>(r.ptr - str.data());
40+
}
41+
}
42+
43+
return val;
44+
}
45+
46+
} // namespace detail
47+
48+
BOOST_DECIMAL_EXPORT inline auto stod32(const std::string& str, std::size_t* idx = nullptr) -> decimal32_t
49+
{
50+
return detail::from_string_impl<decimal32_t>(str, idx);
51+
}
52+
53+
BOOST_DECIMAL_EXPORT inline auto stod32f(const std::string& str, std::size_t* idx = nullptr) -> decimal_fast32_t
54+
{
55+
return detail::from_string_impl<decimal_fast32_t>(str, idx);
56+
}
57+
58+
BOOST_DECIMAL_EXPORT inline auto stod64(const std::string& str, std::size_t* idx = nullptr) -> decimal64_t
59+
{
60+
return detail::from_string_impl<decimal64_t>(str, idx);
61+
}
62+
63+
BOOST_DECIMAL_EXPORT inline auto stod64f(const std::string& str, std::size_t* idx = nullptr) -> decimal_fast64_t
64+
{
65+
return detail::from_string_impl<decimal_fast64_t>(str, idx);
66+
}
67+
68+
BOOST_DECIMAL_EXPORT inline auto stod128(const std::string& str, std::size_t* idx = nullptr) -> decimal128_t
69+
{
70+
return detail::from_string_impl<decimal128_t>(str, idx);
71+
}
72+
73+
BOOST_DECIMAL_EXPORT inline auto stod128f(const std::string& str, std::size_t* idx = nullptr) -> decimal_fast128_t
74+
{
75+
return detail::from_string_impl<decimal_fast128_t>(str, idx);
76+
}
77+
1678
BOOST_DECIMAL_EXPORT template <typename DecimalType>
1779
auto to_string(const DecimalType value)
1880
BOOST_DECIMAL_REQUIRES_RETURN(detail::is_decimal_floating_point_v, DecimalType, std::string)

test/Jamfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ run-fail test_fprintf.cpp ;
149149
run test_frexp_ldexp.cpp ;
150150
run test_from_chars.cpp /boost/charconv//boost_charconv ;
151151
run test_from_chars_nan_payloads.cpp ;
152+
run test_from_string.cpp ;
152153
run test_git_issue_266.cpp ;
153154
run test_git_issue_271.cpp ;
154155
run test_hash.cpp ;

test/test_from_string.cpp

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
// Copyright 2025 Matt Borland
2+
// Distributed under the Boost Software License, Version 1.0.
3+
// https://www.boost.org/LICENSE_1_0.txt
4+
5+
#include <boost/decimal.hpp>
6+
#include <boost/core/lightweight_test.hpp>
7+
#include <random>
8+
#include <string>
9+
10+
using namespace boost::decimal;
11+
12+
static std::mt19937_64 rng {42};
13+
static constexpr std::size_t N {1024};
14+
15+
template <typename T>
16+
T recover_value(const std::string& str, std::size_t* ptr);
17+
18+
template <>
19+
decimal32_t recover_value<decimal32_t>(const std::string& str, std::size_t* ptr)
20+
{
21+
return stod32(str, ptr);
22+
}
23+
24+
template <>
25+
decimal_fast32_t recover_value<decimal_fast32_t>(const std::string& str, std::size_t* ptr)
26+
{
27+
return stod32f(str, ptr);
28+
}
29+
30+
template <>
31+
decimal64_t recover_value<decimal64_t>(const std::string& str, std::size_t* ptr)
32+
{
33+
return stod64(str, ptr);
34+
}
35+
36+
template <>
37+
decimal_fast64_t recover_value<decimal_fast64_t>(const std::string& str, std::size_t* ptr)
38+
{
39+
return stod64f(str, ptr);
40+
}
41+
42+
template <>
43+
decimal128_t recover_value<decimal128_t>(const std::string& str, std::size_t* ptr)
44+
{
45+
return stod128(str, ptr);
46+
}
47+
48+
template <>
49+
decimal_fast128_t recover_value<decimal_fast128_t>(const std::string& str, std::size_t* ptr)
50+
{
51+
return stod128f(str, ptr);
52+
}
53+
54+
template <typename T>
55+
void test()
56+
{
57+
std::uniform_int_distribution<std::int32_t> sig_dist {-9'999'999, 9'999'999};
58+
std::uniform_int_distribution<std::int32_t> exp_dist {-50, 50};
59+
60+
for (std::size_t i {}; i < N; ++i)
61+
{
62+
const T val {sig_dist(rng), exp_dist(rng)};
63+
64+
char buffer[64];
65+
const auto r {to_chars(buffer, buffer + sizeof(buffer), val)};
66+
BOOST_TEST(r);
67+
const auto dist {static_cast<std::size_t>(r.ptr - buffer)};
68+
69+
*r.ptr = '\0';
70+
const std::string str {buffer};
71+
std::size_t idx {};
72+
const T return_value {recover_value<T>(str, &idx)};
73+
74+
BOOST_TEST_EQ(return_value, val);
75+
BOOST_TEST_EQ(idx, dist);
76+
}
77+
}
78+
79+
inline void test_overflow_path()
80+
{
81+
const std::string str {"INF"};
82+
83+
#ifndef BOOST_DECIMAL_DISABLE_EXCEPTIONS
84+
85+
BOOST_TEST_THROWS(recover_value<decimal32_t>(str, nullptr), std::out_of_range);
86+
87+
#else
88+
89+
BOOST_TEST(isnan(recover_value<decimal32_t>(str, nullptr)));
90+
91+
#endif
92+
}
93+
94+
inline void test_invalid_path()
95+
{
96+
const std::string str {"JUNK"};
97+
98+
#ifndef BOOST_DECIMAL_DISABLE_EXCEPTIONS
99+
100+
BOOST_TEST_THROWS(recover_value<decimal32_t>(str, nullptr), std::invalid_argument);
101+
102+
#else
103+
104+
BOOST_TEST(isnan(recover_value<decimal32_t>(str, nullptr)));
105+
106+
#endif
107+
}
108+
109+
int main()
110+
{
111+
test<decimal32_t>();
112+
test<decimal_fast32_t>();
113+
test<decimal64_t>();
114+
test<decimal_fast64_t>();
115+
test<decimal128_t>();
116+
test<decimal_fast128_t>();
117+
118+
test_overflow_path();
119+
test_invalid_path();
120+
121+
return boost::report_errors();
122+
}
123+

0 commit comments

Comments
 (0)