KSeExpr  4.0.4.0
Utils.cpp
Go to the documentation of this file.
1 // SPDX-FileCopyrightText: 2020 L. E. Segovia <amy@amyspark.me>
2 // SPDX-License-Identifier: GPL-3.0-or-later
3 
4 #include "Utils.h"
5 #include <cerrno>
6 
7 #if defined(KSeExpr_HAVE_CHARCONV_WITH_DOUBLES)
8 #include <charconv>
9 #include <cstring>
10 
11 double_t KSeExpr::Utils::atof(const char *num)
12 {
13  double_t v;
14  auto [p, ec] = std::from_chars(num, num + std::strlen(num), v);
15  if (ec == std::errc()) {
16  return v;
17  } else {
18  return HUGE_VAL;
19  }
20 }
21 
22 double_t KSeExpr::Utils::atof(const std::string &num)
23 {
24  double_t v;
25  auto [p, ec] = std::from_chars(num.data(), num.data() + num.size(), v);
26  if (ec == std::errc()) {
27  return v;
28  } else {
29  return HUGE_VAL;
30  }
31 }
32 
33 int32_t KSeExpr::Utils::strtol(const std::string &num)
34 {
35  int32_t v;
36  auto [p, ec] = std::from_chars(num.data(), num.data() + num.size(), v);
37  if (ec == std::errc()) {
38  return v;
39  } else if (ec == std::errc::result_out_of_range) {
40  throw std::out_of_range {"KSeExpr::Utils::strtol: out of range"};
41  } else {
42  throw std::invalid_argument {"KSeExpr::Utils::strtol: impossible to parse the given number"};
43  }
44 }
45 
46 #else
51 double_t KSeExpr::Utils::atof(const char *num)
52 {
53  if (!num) {
54  return 0;
55  }
56 
57  double_t sign = 1;
58  double_t int_part = 0.0;
59  double_t frac_part = 0.0;
60  bool has_frac = false;
61  bool has_exp = false;
62 
63  // +/- sign
64  if (*num == '-') {
65  ++num;
66  sign = -1;
67  } else if (*num == '+') {
68  ++num;
69  }
70 
71  while (*num != '\0') {
72  if (*num >= '0' && *num <= '9') {
73  int_part = int_part * 10 + (*num - '0');
74  } else if (*num == '.') {
75  has_frac = true;
76  ++num;
77  break;
78  } else if (*num == 'e') {
79  has_exp = true;
80  ++num;
81  break;
82  } else {
83  return HUGE_VAL;
84  }
85  ++num;
86  }
87 
88  if (has_frac) {
89  double_t frac_exp = 0.1; // NOLINT readability-magic-numbers
90 
91  while (*num != '\0') {
92  if (*num >= '0' && *num <= '9') {
93  frac_part += frac_exp * (*num - '0');
94  frac_exp *= 0.1; // NOLINT readability-magic-numbers
95  } else if (*num == 'e') {
96  has_exp = true;
97  ++num;
98  break;
99  } else {
100  return HUGE_VAL;
101  }
102  ++num;
103  }
104  }
105 
106  // parsing exponent part
107  double_t exp_part = 1.0;
108  if (*num != '\0' && has_exp) {
109  int exp_sign = 1;
110  if (*num == '-') {
111  exp_sign = -1;
112  ++num;
113  } else if (*num == '+') {
114  ++num;
115  }
116 
117  int e = 0;
118  while (*num != '\0') {
119  if (*num >= '0' && *num <= '9') {
120  e = e * 10 + *num - '0'; // NOLINT readability-magic-numbers
121  } else {
122  return HUGE_VAL;
123  }
124 
125  ++num;
126  }
127 
128  exp_part = pow(exp_sign * e, 10); // NOLINT readability-magic-numbers
129  }
130 
131  return sign * (int_part + frac_part) * exp_part;
132 }
133 
134 double_t KSeExpr::Utils::atof(const std::string &num)
135 {
136  return Utils::atof(num.data());
137 }
138 
139 int32_t KSeExpr::Utils::strtol(const std::string &num)
140 {
141  char *ptr {nullptr};
142  // integer numbers only use dots
143  const auto result {std::strtol(num.c_str(), &ptr, 10)};
144  if (ptr == num.c_str())
145  throw std::invalid_argument {"KSeExpr::Utils::atoi: impossible to parse the given number"};
146  else if (ptr != num.c_str() + num.size())
147  throw std::invalid_argument {"KSeExpr::Utils::atoi: the string had invalid extra characters"};
148  else if (errno == ERANGE)
149  throw std::out_of_range {"KSeExpr::Utils::atoi: out of range"};
150  return result;
151 }
152 
153 #endif // defined(HAVE_CHARCONV_WITH_DOUBLES)
154 
155 // Dynamically dispatchable functions.
156 // These have to be in a namespace, otherwise GCC chokes.
157 // See https://stackoverflow.com/questions/19785010/gcc-function-multiversioning-and-namespaces
158 
159 namespace KSeExpr
160 {
161 namespace Utils
162 {
163 KSeExpr_DEFAULT double_t round(double_t val)
164 {
165  return std::round(val);
166 }
167 
168 KSeExpr_DEFAULT double_t floor(double_t val)
169 {
170  return std::floor(val);
171 }
172 
173 #if defined(KSeExpr_HAVE_DYNAMIC_DISPATCH)
174 KSeExpr_SSE41 double_t round(double_t val)
175 {
176  return _mm_cvtsd_f64(_mm_round_sd(_mm_set_sd(0.0), _mm_set_sd(val), _MM_FROUND_TO_NEAREST_INT));
177 }
178 
179 KSeExpr_SSE41 double_t floor(double_t val)
180 {
181  return _mm_cvtsd_f64(_mm_floor_sd(_mm_set_sd(0.0), _mm_set_sd(val)));
182 }
183 #endif
184 } // namespace Utils
185 } // namespace KSeExpr
#define KSeExpr_DEFAULT
Definition: Utils.h:18
static constexpr std::array< int, 514 > p
Definition: NoiseTables.h:10
KSeExpr_DEFAULT double_t round(double_t val)
Definition: Utils.cpp:163
KSeExpr_DEFAULT double_t floor(double_t val)
Definition: Utils.cpp:168
int32_t strtol(const char *num)
double_t atof(const char *num)
Definition: Utils.cpp:51