YARP
Yet Another Robot Platform
numeric.h
Go to the documentation of this file.
1 /*
2  * SPDX-FileCopyrightText: 2006-2021 Istituto Italiano di Tecnologia (IIT)
3  * SPDX-License-Identifier: BSD-3-Clause
4  */
5 
6 #ifndef YARP_CONFIG_NUMERIC_H
7 #define YARP_CONFIG_NUMERIC_H
8 
9 #define YARP_HAS_SYS_TYPES_H
10 
11 #if defined(YARP_HAS_SYS_TYPES_H)
12 # include <sys/types.h>
13 #endif
14 
15 #include <cfloat>
16 #include <cinttypes>
17 #include <clocale>
18 #include <cstdint>
19 #include <cstddef>
20 #include <cstdlib>
21 #include <cstring>
22 #include <limits>
23 #include <sstream>
24 #include <string>
25 #if defined (_MSC_VER)
26 # include <BaseTsd.h>
27 #endif
28 
29 /* #undef YARP_BIG_ENDIAN */
30 #define YARP_LITTLE_ENDIAN
31 
32 #define YARP_HAS_FLOAT128_T
33 #define YARP_FLOAT32_IS_IEC559 1
34 #define YARP_FLOAT64_IS_IEC559 1
35 #define YARP_FLOAT128_IS_IEC559 1
36 
37 // Ensure that float32_t, float64_t, and float128_t are not defined by some
38 // other header, included before this one.
39 #if defined(float32_t)
40 # undef float32_t
41 #endif
42 #if defined(float64_t)
43 # undef float64_t
44 #endif
45 #if defined(float128_t)
46 # undef float128_t
47 #endif
48 
49 
50 #define YARP_FLT_EXP_DIG 3
51 #define YARP_DBL_EXP_DIG 4
52 #define YARP_LDBL_EXP_DIG 5
53 
54 /*
55  * The maximum string length for a 'double' printed as a string using ("%.*g", DECIMAL_DIG) will be:
56  * Initial +/- sign 1 char
57  * First digit for exponential notation 1 char
58  * '.' decimal separator char 1 char
59  * DECIMAL_DIG digits for the mantissa DECIMAL_DIG chars
60  * 'e+/-' 2 chars
61  * YARP_DBL_EXP_DIG for the exponential YARP_DBL_EXP_DIG chars (YARP_LDBL_EXP_DIG if 128 bit float is available)
62  * string terminator 1 char
63  * FILLER 10 chars (you know, for safety)
64  * -----------------------------------------------------
65  * TOTAL is 16 + DECIMAL_DIG + YARP_DBL_EXP_DIG
66  */
67 #if defined(YARP_HAS_FLOAT128_T)
68 # define YARP_DOUBLE_TO_STRING_MAX_LENGTH (16 + DECIMAL_DIG + YARP_LDBL_EXP_DIG)
69 #else
70 # define YARP_DOUBLE_TO_STRING_MAX_LENGTH (16 + DECIMAL_DIG + YARP_DBL_EXP_DIG)
71 #endif
72 
73 namespace yarp {
74 namespace conf {
75 
76 typedef float float32_t;
77 typedef double float64_t;
78 typedef std::int32_t vocab32_t;
79 #if defined(YARP_HAS_FLOAT128_T)
80 typedef long double float128_t;
81 #endif
82 
83 #if defined (_MSC_VER)
84 typedef ::SSIZE_T ssize_t;
85 #else
87 #endif
88 
89 // Define `clamp` algorithm, available in c++17
90 template<class T>
91 constexpr const T& clamp( const T& v, const T& lo, const T& hi )
92 {
93  // assert( !(hi < lo) );
94  return (v < lo) ? lo : (hi < v) ? hi : v;
95 }
96 
97 template<class T, class Compare>
98 constexpr const T& clamp( const T& v, const T& lo, const T& hi, Compare comp )
99 {
100  // assert( !comp(hi, lo) );
101  return comp(v, lo) ? lo : comp(hi, v) ? hi : v;
102 }
103 
104 namespace numeric {
105 
106 #if !defined(SWIG)
107 
108 /*
109  * Converts an integer number to a string
110  */
111 template <typename IntegerType,
112  std::enable_if_t<
113  std::is_integral<IntegerType>::value &&
114  !std::is_same<IntegerType, bool>::value, bool> = true>
115 inline std::string to_string(IntegerType x)
116 {
117  return std::to_string(x);
118 }
119 
120 /*
121  * Converts a boolean to a string
122  */
123 template <typename BoolType,
124  std::enable_if_t<std::is_same<BoolType, bool>::value, bool> = true>
125 inline std::string to_string(BoolType x)
126 {
127  return {x ? "true" : "false"};
128 }
129 
130 /*
131  * Converts a floating point number to a string, dealing with locale issues
132  */
133 template <typename FloatingType,
134  std::enable_if_t<std::is_floating_point<FloatingType>::value, bool> = true>
135 inline std::string to_string(FloatingType x)
136 {
137  char buf[YARP_DOUBLE_TO_STRING_MAX_LENGTH]; // -> see comment at the top of the file
138  std::snprintf(buf, YARP_DOUBLE_TO_STRING_MAX_LENGTH, "%.*g", DECIMAL_DIG, x);
139  std::string str(buf);
140 
141  // If locale is set, the locale version of the decimal point is used.
142  // In this case we change it to the standard "."
143  // If there is no decimal point, and it is not being used the exponential
144  // notation (i.e. the number is in integer form, for example 100000 and not
145  // 1e5) we add ".0" to ensure that it will be interpreted as a double.
146  struct lconv* lc = localeconv();
147  size_t offset = str.find(lc->decimal_point);
148  if (offset != std::string::npos) {
149  str[offset] = '.';
150  } else if (str.find('e') == std::string::npos && str != "inf" && str != "-inf" && str != "nan") {
151  str += ".0";
152  }
153  return str;
154 }
155 
156 /*
157  * Converts a string to a signed integer number
158  */
159 template <typename IntegerType,
160  std::enable_if_t<
161  std::is_integral<IntegerType>::value &&
162  std::is_signed<IntegerType>::value &&
163  !std::is_same<IntegerType, bool>::value, bool> = true>
164 inline IntegerType from_string(const std::string& src, IntegerType defaultValue = static_cast<IntegerType>(0))
165 {
166  char *endptr = nullptr;
167  const char* startptr = src.c_str();
168  static constexpr int base = 10;
169 
170  errno = 0;
171  auto ret = std::strtoll(startptr, &endptr, base);
172 
173  if (errno != 0) {
174  // An error occurred
175  return defaultValue;
176  }
177 
178  if (endptr == startptr) {
179  // No digits were found
180  return defaultValue;
181  }
182 
183  if (endptr != startptr + src.size()) {
184  // Not all the string was interpreted
185  return defaultValue;
186  }
187 
188  if (ret < std::numeric_limits<IntegerType>::min() || ret > std::numeric_limits<IntegerType>::max()) {
189  // Out of bound
190  return defaultValue;
191  }
192 
193  return static_cast<IntegerType>(ret);
194 }
195 
196 /*
197  * Converts a string to an unsigned integer number
198  */
199 template <typename IntegerType,
200  std::enable_if_t<
201  std::is_integral<IntegerType>::value &&
202  !std::is_signed<IntegerType>::value &&
203  !std::is_same<IntegerType, bool>::value, bool> = true>
204 inline IntegerType from_string(const std::string& src, IntegerType defaultValue = static_cast<IntegerType>(0))
205 {
206  char *endptr = nullptr;
207  const char* startptr = src.c_str();
208  static constexpr int base = 10;
209 
210  errno = 0;
211  auto ret = std::strtoull(startptr, &endptr, base);
212 
213  if (errno != 0) {
214  // An error occurred
215  return defaultValue;
216  }
217 
218  if (endptr == startptr) {
219  // No digits were found
220  return defaultValue;
221  }
222 
223  if (endptr != startptr + src.size()) {
224  // Not all the string was interpreted
225  return defaultValue;
226  }
227 
228  if (ret > std::numeric_limits<IntegerType>::max()) {
229  // Out of bound
230  return defaultValue;
231  }
232 
233  if (ret > std::numeric_limits<long long>::max() && std::strtoll(startptr, &endptr, base) < 0) {
234  // This is a negative number silently converted to unsigned
235  return defaultValue;
236  }
237 
238  return static_cast<IntegerType>(ret);
239 }
240 
241 
242 /*
243  * Converts a string to a boolean
244  */
245 template <typename BoolType,
246  std::enable_if_t<std::is_same<BoolType, bool>::value, bool> = true>
247 inline BoolType from_string(const std::string& src, BoolType defaultValue = false)
248 {
249  if (src == "1") { return true; }
250  if (src == "true") { return true; }
251  if (src == "True") { return true; }
252  if (src == "TRUE") { return true; }
253  if (src == "yes") { return true; }
254  if (src == "Yes") { return true; }
255  if (src == "YES") { return true; }
256  if (src == "on") { return true; }
257  if (src == "On") { return true; }
258  if (src == "ON") { return true; }
259 
260  if (src == "0") { return false; }
261  if (src == "false") { return false; }
262  if (src == "False") { return false; }
263  if (src == "FALSE") { return false; }
264  if (src == "no") { return false; }
265  if (src == "No") { return false; }
266  if (src == "NO") { return false; }
267  if (src == "off") { return false; }
268  if (src == "Off") { return false; }
269  if (src == "OFF") { return false; }
270 
271  return defaultValue;
272 }
273 
274 /*
275  * Converts a string to a floating point number, dealing with locale issues
276  */
277 template <typename FloatingType,
278  std::enable_if_t<std::is_floating_point<FloatingType>::value, bool> = true>
279 inline FloatingType from_string(const std::string& src, FloatingType defaultValue = static_cast<FloatingType>(0.0))
280 {
281  if (src == "inf") {
282  return std::numeric_limits<FloatingType>::infinity();
283  }
284  if (src == "-inf") {
285  return -std::numeric_limits<FloatingType>::infinity();
286  }
287  if (src == "nan") {
288  return std::numeric_limits<FloatingType>::quiet_NaN();
289  }
290  // Need to deal with alternate versions of the decimal point.
291  // We need a copy of the string where the decimal point can be replaced.
292  std::string src_c = src;
293  size_t offset = src.find('.');
294  if (offset != std::string::npos) {
295  struct lconv* lc = localeconv();
296  src_c[offset] = lc->decimal_point[0];
297  }
298 
299  char *endptr = nullptr;
300  const char* startptr = src_c.c_str();
301  auto ret = static_cast<FloatingType>(strtod(startptr, &endptr));
302 
303  if (endptr == startptr) {
304  // No digits were found
305  return defaultValue;
306  }
307 
308  if (endptr != startptr + src.size()) {
309  // Not all the string was interpreted
310  return defaultValue;
311  }
312 
313  return ret;
314 }
315 
316 template <typename IntegerType,
317  std::enable_if_t<
318  std::is_integral<IntegerType>::value &&
319  sizeof(IntegerType) != 1 &&
320  !std::is_same<IntegerType, bool>::value, bool> = true>
321 std::string to_hex_string(IntegerType i)
322 {
323  std::stringstream stream;
324  stream << std::hex << i;
325  return stream.str();
326 }
327 
328 // FIXME C++17 use constexpr if
329 template <typename IntegerType,
330  std::enable_if_t<sizeof(IntegerType) == 1, bool> = true>
331 std::string to_hex_string(IntegerType i)
332 {
333  // char and unsigned char are interpreted as characters, and therefore
334  // printed as the corresponding character
335  return to_hex_string<int>(static_cast<int>(static_cast<uint8_t>(i)));
336 }
337 
338 #endif // !defined(SWIG)
339 
340 } // namespace numeric
341 } // namespace conf
342 } // namespace yarp
343 
344 
345 #ifndef YARP_NO_DEPRECATED // since YARP 3.0.0
346 #include <yarp/conf/api.h> // For YARP_DEPRECATED_TYPEDEF_MSG
347 YARP_DEPRECATED_TYPEDEF_MSG("Use std::int8_t instead") std::int8_t YARP_INT8;
348 YARP_DEPRECATED_TYPEDEF_MSG("Use std::int16_t instead") std::int16_t YARP_INT16;
349 YARP_DEPRECATED_TYPEDEF_MSG("Use std::int32_t instead") std::int32_t YARP_INT32;
350 YARP_DEPRECATED_TYPEDEF_MSG("Use std::int64_t instead") std::int64_t YARP_INT64;
354 #define YARP_INT32_FMT PRId32
355 #define YARP_INT64_FMT PRId64
356 #endif // YARP_NO_DEPRECATED
357 
358 
359 #endif
bool ret
std::string to_hex_string(IntegerType i)
Definition: numeric.h:321
IntegerType from_string(const std::string &src, IntegerType defaultValue=static_cast< IntegerType >(0))
Definition: numeric.h:164
std::string to_string(FloatingType x)
Definition: numeric.h:135
std::string to_string(IntegerType x)
Definition: numeric.h:115
std::int32_t vocab32_t
Definition: numeric.h:78
::ssize_t ssize_t
Definition: numeric.h:86
double float64_t
Definition: numeric.h:77
long double float128_t
Definition: numeric.h:80
constexpr const T & clamp(const T &v, const T &lo, const T &hi)
Definition: numeric.h:91
float float32_t
Definition: numeric.h:76
The main, catch-all namespace for YARP.
Definition: dirs.h:16
yarp::conf::float32_t YARP_FLOAT32
Definition: numeric.h:351
std::int8_t YARP_INT8
Definition: numeric.h:347
std::int64_t YARP_INT64
Definition: numeric.h:350
std::int16_t YARP_INT16
Definition: numeric.h:348
std::int32_t YARP_INT32
Definition: numeric.h:349
#define YARP_DOUBLE_TO_STRING_MAX_LENGTH
Definition: numeric.h:68
yarp::conf::ssize_t YARP_SSIZE_T
Definition: numeric.h:353
yarp::conf::float64_t YARP_FLOAT64
Definition: numeric.h:352
#define YARP_DEPRECATED_TYPEDEF_MSG(x)
Definition: api.h:99