1// Copyright 2024 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/charconv.hpp>
6#include <boost/core/lightweight_test.hpp>
7#include <boost/random/uniform_real_distribution.hpp>
8#include <system_error>
9#include <limits>
10#include <random>
11#include <array>
12#include <cstdint>
13#include <iomanip>
14
15constexpr std::size_t N = 1024;
16static std::mt19937_64 rng(42);
17
18template <typename T>
19void test_non_finite()
20{
21 constexpr std::array<T, 6> values = {{std::numeric_limits<T>::infinity(), -std::numeric_limits<T>::infinity(),
22 std::numeric_limits<T>::quiet_NaN(), -std::numeric_limits<T>::quiet_NaN(),
23 std::numeric_limits<T>::signaling_NaN(), -std::numeric_limits<T>::signaling_NaN()}};
24
25 for (const auto val : values)
26 {
27 char buffer[2];
28 auto r = boost::charconv::to_chars(buffer, buffer + sizeof(buffer), val);
29 BOOST_TEST(r.ec == std::errc::value_too_large);
30 }
31
32 char inf_buffer[3];
33 constexpr T inf_val = std::numeric_limits<T>::infinity();
34 auto r_inf = boost::charconv::to_chars(inf_buffer, inf_buffer + 3, inf_val);
35 BOOST_TEST(r_inf);
36 BOOST_TEST(!std::memcmp(inf_buffer, "inf", 3));
37
38 char nan_buffer[3];
39 constexpr T nan_val = std::numeric_limits<T>::quiet_NaN();
40 auto r_nan = boost::charconv::to_chars(nan_buffer, nan_buffer + 3, nan_val);
41 BOOST_TEST(r_nan);
42 BOOST_TEST(!std::memcmp(nan_buffer, "nan", 3));
43
44 char neg_nan_buffer[9];
45 auto r_neg_nan = boost::charconv::to_chars(neg_nan_buffer, neg_nan_buffer + 9, -nan_val);
46 BOOST_TEST(r_neg_nan);
47 BOOST_TEST(!std::memcmp(neg_nan_buffer, "-nan(ind)", 9));
48
49 char snan_buffer[9];
50 constexpr T snan_val = std::numeric_limits<T>::signaling_NaN();
51 auto r_snan = boost::charconv::to_chars(snan_buffer, snan_buffer + 9, snan_val);
52 BOOST_TEST(r_snan);
53 BOOST_TEST(!std::memcmp(snan_buffer, "nan(snan)", 9));
54}
55
56template <typename T>
57void test_non_finite_fixed_precision()
58{
59 constexpr std::array<T, 6> values = {{std::numeric_limits<T>::infinity(), -std::numeric_limits<T>::infinity(),
60 std::numeric_limits<T>::quiet_NaN(), -std::numeric_limits<T>::quiet_NaN(),
61 std::numeric_limits<T>::signaling_NaN(), -std::numeric_limits<T>::signaling_NaN()}};
62
63 const auto formats = {boost::charconv::chars_format::scientific, boost::charconv::chars_format::hex,
64 boost::charconv::chars_format::general, boost::charconv::chars_format::fixed};
65
66 for (auto format : formats)
67 {
68 for (const auto val : values)
69 {
70 char buffer[2];
71 auto r = boost::charconv::to_chars(buffer, buffer + sizeof(buffer), val, format, 5);
72 BOOST_TEST(r.ec == std::errc::value_too_large);
73 }
74
75 char inf_buffer[3];
76 constexpr T inf_val = std::numeric_limits<T>::infinity();
77 auto r_inf = boost::charconv::to_chars(inf_buffer, inf_buffer + 3, inf_val, format, 5);
78 BOOST_TEST(r_inf);
79 BOOST_TEST(!std::memcmp(inf_buffer, "inf", 3));
80
81 char nan_buffer[3];
82 constexpr T nan_val = std::numeric_limits<T>::quiet_NaN();
83 auto r_nan = boost::charconv::to_chars(nan_buffer, nan_buffer + 3, nan_val, format, 5);
84 BOOST_TEST(r_nan);
85 BOOST_TEST(!std::memcmp(nan_buffer, "nan", 3));
86
87 char neg_nan_buffer[9];
88 auto r_neg_nan = boost::charconv::to_chars(neg_nan_buffer, neg_nan_buffer + 9, -nan_val, format, 5);
89 BOOST_TEST(r_neg_nan);
90 BOOST_TEST(!std::memcmp(neg_nan_buffer, "-nan(ind)", 9));
91
92 char snan_buffer[9];
93 constexpr T snan_val = std::numeric_limits<T>::signaling_NaN();
94 auto r_snan = boost::charconv::to_chars(snan_buffer, snan_buffer + 9, snan_val, format, 5);
95 BOOST_TEST(r_snan);
96 BOOST_TEST(!std::memcmp(snan_buffer, "nan(snan)", 9));
97 }
98};
99
100#ifdef BOOST_MSVC
101# pragma warning(push)
102# pragma warning(disable: 4127)
103#endif
104
105template <typename T>
106void test_min_buffer_size()
107{
108 #if defined(_WIN32)
109 std::uniform_real_distribution<T> dist((std::numeric_limits<T>::min)(), (std::numeric_limits<T>::max)());
110 #else
111 std::uniform_real_distribution<T> dist((std::numeric_limits<T>::lowest)(), (std::numeric_limits<T>::max)());
112 #endif
113
114 // No guarantees are made for fixed, especially in this domain
115 auto formats = {boost::charconv::chars_format::hex,
116 boost::charconv::chars_format::scientific,
117 boost::charconv::chars_format::general};
118
119 int format_int = 0;
120 for (const auto format : formats)
121 {
122 for (std::size_t i = 0; i < N; ++i)
123 {
124 char buffer[boost::charconv::limits<T>::max_chars10];
125 const T value = dist(rng);
126
127 if (!std::isnormal(value))
128 {
129 continue;
130 }
131
132 auto r = boost::charconv::to_chars(buffer, buffer + sizeof(buffer), value, format);
133 if (!BOOST_TEST(r))
134 {
135 // LCOV_EXCL_START
136 std::cerr << std::setprecision(std::numeric_limits<T>::max_digits10) << "Overflow for: " << value
137 << "\nFormat: " << format_int
138 << "\nBuffer size: " << sizeof(buffer) << std::endl;
139 // LCOV_EXCL_STOP
140 }
141 }
142 ++format_int;
143 }
144}
145
146#ifdef BOOST_MSVC
147# pragma warning(pop)
148#endif
149
150#if BOOST_CHARCONV_LDBL_BITS > 64
151void test_failed_values()
152{
153 // No guarantees are made for fixed, especially in this domain
154 // TODO(mborland): Add hex once https://github.com/boostorg/charconv/issues/154 is fixed
155 auto formats = {boost::charconv::chars_format::scientific,
156 boost::charconv::chars_format::general};
157
158 std::array<long double, 2> failed_values = {._M_elems: {6.93880126833169422964e+4931L, 9.14517491001980558957e+4931L}};
159
160 int format_int = 0;
161 for (const auto format : formats)
162 {
163 for (const auto value : failed_values)
164 {
165 char buffer[boost::charconv::limits<long double>::max_chars10];
166
167 if (!std::isnormal(x: value))
168 {
169 continue;
170 }
171
172 auto r = boost::charconv::to_chars(first: buffer, last: buffer + sizeof(buffer), value, fmt: format);
173 if (!BOOST_TEST(r))
174 {
175 // LCOV_EXCL_START
176 std::cerr << std::setprecision(std::numeric_limits<long double>::max_digits10) << "Overflow for: " << value
177 << "\nFormat: " << format_int
178 << "\nBuffer size: " << sizeof(buffer) << std::endl;
179 // LCOV_EXCL_STOP
180 }
181 }
182 ++format_int;
183 }
184}
185#endif
186
187int main()
188{
189 test_non_finite<float>();
190 test_non_finite<double>();
191 test_non_finite<long double>();
192 #ifdef BOOST_CHARCONV_HAS_FLOAT16
193 test_non_finite<std::float16_t>();
194 #endif
195 #ifdef BOOST_CHARCONV_HAS_FLOAT32
196 test_non_finite<std::float32_t>();
197 #endif
198 #ifdef BOOST_CHARCONV_HAS_FLOAT64
199 test_non_finite<std::float64_t>();
200 #endif
201 #ifdef BOOST_CHARCONV_HAS_BRAINFLOAT16
202 test_non_finite<std::bfloat16_t>();
203 #endif
204
205 test_non_finite_fixed_precision<float>();
206 test_non_finite_fixed_precision<double>();
207 test_non_finite_fixed_precision<long double>();
208 #ifdef BOOST_CHARCONV_HAS_FLOAT16
209 test_non_finite_fixed_precision<std::float16_t>();
210 #endif
211 #ifdef BOOST_CHARCONV_HAS_FLOAT32
212 test_non_finite_fixed_precision<std::float32_t>();
213 #endif
214 #ifdef BOOST_CHARCONV_HAS_FLOAT64
215 test_non_finite_fixed_precision<std::float64_t>();
216 #endif
217 #ifdef BOOST_CHARCONV_HAS_BRAINFLOAT16
218 test_non_finite_fixed_precision<std::bfloat16_t>();
219 #endif
220
221 test_min_buffer_size<float>();
222 test_min_buffer_size<double>();
223 test_min_buffer_size<long double>();
224 #ifdef BOOST_CHARCONV_HAS_FLOAT32
225 test_min_buffer_size<std::float32_t>();
226 #endif
227 #ifdef BOOST_CHARCONV_HAS_FLOAT64
228 test_min_buffer_size<std::float64_t>();
229 #endif
230
231 #if BOOST_CHARCONV_LDBL_BITS > 64
232 test_failed_values();
233 #endif
234
235 return boost::report_errors();
236}
237

source code of boost/libs/charconv/test/github_issue_152.cpp