Skip to content

Commit 1296489

Browse files
authored
Merge pull request #361 from cppalliance/to_string
Add to_string
2 parents 4b471db + 7f5142c commit 1296489

File tree

4 files changed

+251
-22
lines changed

4 files changed

+251
-22
lines changed

include/boost/decimal/detail/io.hpp

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <boost/decimal/detail/parser.hpp>
1212
#include <boost/decimal/detail/attributes.hpp>
1313
#include <boost/decimal/detail/fenv_rounding.hpp>
14+
#include <boost/decimal/detail/to_string.hpp>
1415

1516
#if !defined(BOOST_DECIMAL_DISABLE_CLIB)
1617

@@ -87,28 +88,6 @@ auto operator>>(std::basic_istream<charT, traits>& is, DecimalType& d)
8788
# pragma GCC diagnostic ignored "-Wformat-truncation"
8889
#endif
8990

90-
namespace detail {
91-
92-
template <typename DecimalType, typename Integer, std::enable_if_t<!std::is_same<DecimalType, decimal128>::value, bool> = true>
93-
void print_buffer(char* buffer, std::size_t buffer_size, const char* format, Integer significand)
94-
{
95-
std::snprintf(buffer, buffer_size, format, significand);
96-
}
97-
98-
template <typename DecimalType, typename Integer, std::enable_if_t<std::is_same<DecimalType, decimal128>::value, bool> = true>
99-
void print_buffer(char* buffer, std::size_t buffer_size, const char*, Integer significand)
100-
{
101-
char local_buffer [64];
102-
const auto p {detail::emulated128_to_buffer(local_buffer, significand)};
103-
const auto print_size {static_cast<std::size_t>(local_buffer + 64 - p)};
104-
if (print_size <= buffer_size)
105-
{
106-
std::memcpy(buffer, p, print_size);
107-
}
108-
}
109-
110-
} //namespace detail
111-
11291
// 3.2.11 Formatted output
11392
template <typename charT, typename traits, BOOST_DECIMAL_DECIMAL_FLOATING_TYPE DecimalType>
11493
auto operator<<(std::basic_ostream<charT, traits>& os, const DecimalType& d)
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
// Copyright 2023 Matt Borland
2+
// Distributed under the Boost Software License, Version 1.0.
3+
// https://www.boost.org/LICENSE_1_0.txt
4+
5+
6+
#ifndef BOOST_DECIMAL_DETAIL_TO_STRING
7+
#define BOOST_DECIMAL_DETAIL_TO_STRING
8+
9+
#include <boost/decimal/fwd.hpp>
10+
#include <boost/decimal/detail/config.hpp>
11+
#include <boost/decimal/detail/type_traits.hpp>
12+
#include <boost/decimal/detail/parser.hpp>
13+
#include <boost/decimal/detail/attributes.hpp>
14+
#include <boost/decimal/detail/fenv_rounding.hpp>
15+
#include <boost/decimal/detail/concepts.hpp>
16+
#include <boost/decimal/detail/cmath/frexp10.hpp>
17+
18+
#if !defined(BOOST_DECIMAL_DISABLE_CLIB)
19+
20+
#include <cerrno>
21+
#include <cstring>
22+
#include <cinttypes>
23+
#include <limits>
24+
#include <ios>
25+
#include <iostream>
26+
#include <system_error>
27+
#include <type_traits>
28+
#include <string>
29+
30+
namespace boost {
31+
namespace decimal {
32+
33+
// GCC UBSAN warns of format truncation from the constexpr calculation of the format
34+
// This warning was added in GCC 7.1
35+
#if __GNUC__ >= 7
36+
# pragma GCC diagnostic push
37+
# pragma GCC diagnostic ignored "-Wformat-truncation"
38+
#endif
39+
40+
namespace detail {
41+
42+
template <typename DecimalType, typename Integer, std::enable_if_t<!std::is_same<DecimalType, decimal128>::value, bool> = true>
43+
void print_buffer(char* buffer, std::size_t buffer_size, const char* format, Integer significand)
44+
{
45+
std::snprintf(buffer, buffer_size, format, significand);
46+
}
47+
48+
template <typename DecimalType, typename Integer, std::enable_if_t<std::is_same<DecimalType, decimal128>::value, bool> = true>
49+
void print_buffer(char* buffer, std::size_t buffer_size, const char*, Integer significand)
50+
{
51+
char local_buffer [64];
52+
const auto p {detail::emulated128_to_buffer(local_buffer, significand)};
53+
const auto print_size {static_cast<std::size_t>(local_buffer + 64 - p)};
54+
if (print_size <= buffer_size)
55+
{
56+
std::memcpy(buffer, p, print_size);
57+
}
58+
}
59+
60+
} //namespace detail
61+
62+
template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE DecimalType>
63+
auto to_string(DecimalType value) -> std::string
64+
{
65+
std::string res;
66+
67+
const bool d_isneg {signbit(value)};
68+
69+
if (issignaling(value))
70+
{
71+
if (d_isneg)
72+
{
73+
res += "-";
74+
}
75+
76+
res += "nan(snan)";
77+
return res;
78+
}
79+
80+
if (isnan(value)) // only quiet NaNs left
81+
{
82+
if (d_isneg)
83+
{
84+
res += "-nan(ind)";
85+
}
86+
else
87+
{
88+
res += "nan";
89+
}
90+
91+
return res;
92+
}
93+
94+
if (isinf(value))
95+
{
96+
if (d_isneg)
97+
{
98+
res += "-";
99+
}
100+
101+
res += "inf";
102+
return res;
103+
}
104+
105+
auto precision {std::numeric_limits<DecimalType>::digits10};
106+
107+
char buffer[detail::precision_v<DecimalType> + 6] {}; // Sign + Precision + decimal point + e + sign + null terminator
108+
109+
const bool isneg {value < 0};
110+
if (isneg)
111+
{
112+
res += "-";
113+
}
114+
115+
constexpr auto format {std::is_same<DecimalType, decimal32>::value ? "%" PRIu32 : "%" PRIu64};
116+
117+
int exp {};
118+
auto significand = frexp10(value, &exp);
119+
120+
auto significand_digits {detail::num_digits(significand)};
121+
const bool reduced {significand_digits > precision};
122+
while (significand_digits > precision + 1)
123+
{
124+
significand /= 10;
125+
++exp;
126+
--significand_digits;
127+
}
128+
129+
if (reduced)
130+
{
131+
exp += detail::fenv_round<DecimalType>(significand, isneg);
132+
}
133+
134+
// Print the significand into the buffer so that we can insert the decimal point
135+
detail::print_buffer<DecimalType>(buffer, sizeof(buffer), format, significand);
136+
std::memmove(buffer + 2, buffer + 1, static_cast<std::size_t>(precision - 1));
137+
std::memset(buffer + 1, '.', 1);
138+
res += buffer;
139+
140+
// Offset will adjust the exponent to compensate for adding the decimal point
141+
const auto offset {detail::num_digits(significand) - 1};
142+
if (offset == 0)
143+
{
144+
res += "0";
145+
}
146+
147+
auto end_it {res.end()};
148+
--end_it;
149+
while (*end_it == '0')
150+
{
151+
--end_it;
152+
}
153+
if (*end_it == '.')
154+
{
155+
--end_it;
156+
}
157+
++end_it;
158+
159+
if (end_it != res.end())
160+
{
161+
res.erase(end_it, res.end());
162+
}
163+
164+
auto print_exp {exp + offset};
165+
if (print_exp != 0)
166+
{
167+
res += "e";
168+
169+
if (print_exp < 0)
170+
{
171+
res += "-";
172+
print_exp = -print_exp;
173+
}
174+
else
175+
{
176+
res += "+";
177+
}
178+
179+
if (print_exp < 10)
180+
{
181+
res += "0";
182+
}
183+
184+
res += std::to_string(print_exp);
185+
}
186+
187+
return res;
188+
}
189+
190+
#if __GNUC__ >= 7
191+
# pragma GCC diagnostic pop
192+
#endif
193+
194+
} //namespace decimal
195+
} //namespace boost
196+
197+
#endif
198+
199+
#endif //BOOST_DECIMAL_DETAIL_TO_STRING

test/Jamfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,3 +67,4 @@ run test_strtod.cpp ;
6767
run test_tan.cpp ;
6868
run test_tanh.cpp ;
6969
run test_tgamma.cpp ;
70+
run test_to_string.cpp ;

test/test_to_string.cpp

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Copyright 2023 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+
8+
using namespace boost::decimal;
9+
10+
#if !defined(BOOST_DECIMAL_DISABLE_CLIB)
11+
12+
template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE T>
13+
void test()
14+
{
15+
BOOST_TEST_EQ(to_string(T{1}), "1");
16+
BOOST_TEST_EQ(to_string(T{10}), "1e+01");
17+
BOOST_TEST_EQ(to_string(T{100}), "1e+02");
18+
BOOST_TEST_EQ(to_string(T{1000}), "1e+03");
19+
BOOST_TEST_EQ(to_string(T{10000}), "1e+04");
20+
BOOST_TEST_EQ(to_string(T{210000}), "2.1e+05");
21+
BOOST_TEST_EQ(to_string(T{2100000}), "2.1e+06");
22+
BOOST_TEST_EQ(to_string(T{21, 6, true}), "-2.1e+07");
23+
BOOST_TEST_EQ(to_string(T{211, 6, true}), "-2.11e+08");
24+
BOOST_TEST_EQ(to_string(T{2111, 6, true}), "-2.111e+09");
25+
26+
BOOST_TEST_EQ(to_string(std::numeric_limits<T>::infinity()), "inf");
27+
BOOST_TEST_EQ(to_string(-std::numeric_limits<T>::infinity()), "-inf");
28+
BOOST_TEST_EQ(to_string(std::numeric_limits<T>::quiet_NaN()), "nan");
29+
BOOST_TEST_EQ(to_string(-std::numeric_limits<T>::quiet_NaN()), "-nan(ind)");
30+
BOOST_TEST_EQ(to_string(std::numeric_limits<T>::signaling_NaN()), "nan(snan)");
31+
BOOST_TEST_EQ(to_string(-std::numeric_limits<T>::signaling_NaN()), "-nan(snan)");
32+
}
33+
34+
int main()
35+
{
36+
test<decimal32>();
37+
test<decimal64>();
38+
test<decimal128>();
39+
40+
return boost::report_errors();
41+
}
42+
43+
#else
44+
45+
int main()
46+
{
47+
return 0;
48+
}
49+
50+
#endif

0 commit comments

Comments
 (0)