dconv 1.0
C++14 library for printing and parsing floating point numbers
Loading...
Searching...
No Matches
atod.hpp
Go to the documentation of this file.
1
25#ifndef __DCONV_ATOD_HPP__
26#define __DCONV_ATOD_HPP__
27
28// dconv.
29#include <dconv/atodpow.hpp>
30#include <dconv/view.hpp>
31
32// C++.
33#include <limits>
34#include <memory>
35
36// C.
37#include <cstring>
38#include <cstdint>
39#include <cstdlib>
40#include <cmath>
41
42namespace dconv
43{
44 namespace details
45 {
47 {
48 constexpr LocaleDelete () noexcept = default;
49
50 void operator () (locale_t loc) noexcept
51 {
52 freelocale (loc);
53 }
54 };
55
56 using LocalePtr = std::unique_ptr <std::remove_pointer_t <locale_t>, LocaleDelete>;
57
58 inline const char * strtodSlow (const char * beg, double& value)
59 {
60 static LocalePtr locale (newlocale (LC_ALL_MASK, "C", nullptr));
61 char* end = nullptr;
62 value = strtod_l (beg, &end, locale.get ());
63 return end;
64 }
65
66 inline void umul192 (uint64_t hi, uint64_t lo, uint64_t significand, uint64_t& high, uint64_t& middle, uint64_t& low) noexcept
67 {
68 #if defined(__SIZEOF_INT128__)
69 __uint128_t h = static_cast <__uint128_t> (hi) * significand;
70 __uint128_t l = static_cast <__uint128_t> (lo) * significand;
71 __uint128_t s = h + (l >> 64);
72
73 high = static_cast <uint64_t> (s >> 64);
74 middle = static_cast <uint64_t> (s);
75 low = static_cast <uint64_t> (l);
76 #else
77 uint64_t hi_hi, hi_lo, lo_hi, lo_lo;
78
79 uint64_t m_lo = static_cast <uint32_t> (significand);
80 uint64_t m_hi = significand >> 32;
81 uint64_t p0 = (hi & 0xFFFFFFFF) * m_lo;
82 uint64_t p1 = (hi >> 32) * m_lo;
83 uint64_t p2 = (hi & 0xFFFFFFFF) * m_hi;
84 uint64_t p3 = (hi >> 32) * m_hi;
85 uint64_t carry = (p0 >> 32) + (p1 & 0xFFFFFFFF) + (p2 & 0xFFFFFFFF);
86 hi_lo = (carry << 32) | (p0 & 0xFFFFFFFF);
87 hi_hi = (carry >> 32) + (p1 >> 32) + (p2 >> 32) + p3;
88
89 p0 = (lo & 0xFFFFFFFF) * m_lo;
90 p1 = (lo >> 32) * m_lo;
91 p2 = (lo & 0xFFFFFFFF) * m_hi;
92 p3 = (lo >> 32) * m_hi;
93 carry = (p0 >> 32) + (p1 & 0xFFFFFFFF) + (p2 & 0xFFFFFFFF);
94 lo_lo = (carry << 32) | (p0 & 0xFFFFFFFF);
95 lo_hi = (carry >> 32) + (p1 >> 32) + (p2 >> 32) + p3;
96
97 low = lo_lo;
98 middle = hi_lo + lo_hi;
99 high = hi_hi + (middle < hi_lo ? 1 : 0);
100 #endif
101 }
102
103 inline bool strtodFast (bool negative, uint64_t significand, int64_t exponent, double& value) noexcept
104 {
105 static constexpr double pow10[] = {
106 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10,
107 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21,
108 1e22
109 };
110
111 value = static_cast <double> (significand);
112
113 if (unlikely((exponent > 22) && (exponent < (22 + 16))))
114 {
115 value *= pow10[exponent - 22];
116 exponent = 22;
117 }
118
119 if (likely ((exponent >= -22) && (exponent <= 22) && (value <= 9007199254740991.0)))
120 {
121 value = (exponent < 0) ? (value / pow10[-exponent]) : (value * pow10[exponent]);
122 value = negative ? -value : value;
123 return true;
124 }
125
126 if (unlikely (value == 0.0))
127 {
128 value = negative ? -0.0 : 0.0;
129 return true;
130 }
131
132 if (unlikely (exponent < -325 || exponent > 308))
133 {
134 return false;
135 }
136
137 uint64_t high, middle, low;
138 const Power& power = atodpow[exponent + 325];
139 umul192 (power.hi, power.lo, significand, high, middle, low);
140 int64_t exp = ((exponent * 217706) >> 16) + 1087;
141
142 int lz;
143 if (high != 0)
144 {
145 lz = __builtin_clzll (high);
146 exp -= lz;
147 }
148 else if (middle != 0)
149 {
150 lz = __builtin_clzll (middle);
151 exp -= lz + 64;
152 }
153 else
154 {
155 return false;
156 }
157
158 if (unlikely (exp <= 0 || exp >= 2047))
159 {
160 return false;
161 }
162
163 if (high == 0)
164 {
165 high = middle << lz;
166 middle = 0;
167 }
168 else if (lz != 0)
169 {
170 high = (high << lz) | (middle >> (64 - lz));
171 middle <<= lz;
172 }
173
174 middle |= (low != 0);
175
176 uint64_t mant = (high >> 11) & 0xFFFFFFFFFFFFF;
177 uint64_t bits = (static_cast <uint64_t> (exp) << 52) | mant;
178 uint64_t frac = high & 0x7FF;
179
180 bool roundUp = ((frac > 0x400) |
181 ((frac == 0x400) && ((middle != 0) || (mant & 1))) |
182 ((frac == 0x3FF) && ((middle != 0))));
183
184 bits += roundUp;
185 bits |= (static_cast <uint64_t> (negative) << 63);
186 std::memcpy (&value, &bits, sizeof (double));
187
188 return true;
189 }
190
191 inline constexpr bool isDigit (char c) noexcept
192 {
193 return static_cast <unsigned char> (c - '0') <= 9u;
194 }
195
196 inline constexpr bool isSign (char c) noexcept
197 {
198 return (c == '+') || (c == '-');
199 }
200
201 inline const char * atod (View& view, double& value)
202 {
203 auto beg = view.data ();
204
205 uint64_t significand = 0;
206 uint64_t digits = 0;
207 bool neg = view.getIf ('-');
208
209 if (view.getIf ('0'))
210 {
211 if (unlikely (isDigit (view.peek ())))
212 {
213 return nullptr;
214 }
215 }
216 else if (likely (isDigit (view.peek ())))
217 {
218 significand = view.get () - '0';
219 ++digits;
220
221 while (isDigit (view.peek ()))
222 {
223 uint64_t next = (10 * significand) + (view.get () - '0');
224 if (unlikely (next < significand)) // overflow
225 {
226 return strtodSlow (beg, value);
227 }
228 significand = next;
229 ++digits;
230 }
231 }
232 else if (view.getIfNoCase ('i') && view.getIfNoCase ('n') && view.getIfNoCase ('f'))
233 {
234 if (view.getIfNoCase ('i'))
235 {
236 if (!(view.getIfNoCase ('n') && view.getIfNoCase ('i') &&
237 view.getIfNoCase ('t') && view.getIfNoCase ('y')))
238 {
239 return nullptr;
240 }
241 }
242 value = neg ? -std::numeric_limits <double>::infinity () : std::numeric_limits <double>::infinity ();
243 return view.data ();
244 }
245 else if (view.getIfNoCase ('n') && view.getIfNoCase ('a') && view.getIfNoCase ('n'))
246 {
247 value = neg ? -std::numeric_limits <double>::quiet_NaN () : std::numeric_limits <double>::quiet_NaN ();
248 return view.data ();
249 }
250 else
251 {
252 return nullptr;
253 }
254
255 int64_t exponent = 0;
256
257 if (view.getIf ('.'))
258 {
259 if (unlikely (!isDigit (view.peek ())))
260 {
261 return nullptr;
262 }
263
264 significand = (10 * significand) + (view.get () - '0');
265 if (significand || digits) ++digits;
266 --exponent;
267
268 while (isDigit (view.peek ()))
269 {
270 uint64_t next = (10 * significand) + (view.get () - '0');
271 if (unlikely (next < significand)) // overflow
272 {
273 return strtodSlow (beg, value);
274 }
275 significand = next;
276 if (significand || digits) ++digits;
277 --exponent;
278 }
279 }
280
281 if (view.getIf ('e') || view.getIf ('E'))
282 {
283 bool negExp = false;
284
285 if (isSign (view.peek ()))
286 {
287 negExp = (view.get () == '-');
288 }
289
290 if (unlikely (!isDigit (view.peek ())))
291 {
292 return nullptr;
293 }
294
295 int64_t exp = view.get () - '0';
296
297 while (isDigit (view.peek ()))
298 {
299 if (likely (exp < 1000))
300 {
301 exp = (10 * exp) + (view.get () - '0');
302 }
303 else
304 {
305 view.get ();
306 }
307 }
308
309 exponent += (negExp ? -exp : exp);
310 }
311
312 if (likely (digits <= 19))
313 {
314 if (strtodFast (neg, significand, exponent, value))
315 {
316 return view.data ();
317 }
318 }
319
320 return strtodSlow (beg, value);
321 }
322 }
323
330 inline const char* atod (const char* str, double& value)
331 {
332 View view (str);
333 return details::atod (view, value);
334 }
335
343 inline const char* atod (const char* str, size_t length, double& value)
344 {
345 View view (str, length);
346 return details::atod (view, value);
347 }
348
356 inline const char* atod (const char* first, const char* last, double& value)
357 {
358 View view (first, last);
359 return details::atod (view, value);
360 }
361}
362
363#endif
char array view.
Definition view.hpp:44
int peek() const noexcept
get character without extracting it.
Definition view.hpp:113
bool getIf(char expected) noexcept
extracts expected character (case sensitive).
Definition view.hpp:140
const char * data() const noexcept
returns a pointer to the first character of a view.
Definition view.hpp:173
bool getIfNoCase(char expected) noexcept
extracts expected character (case insensitive, ASCII-only).
Definition view.hpp:155
int get() noexcept
extracts character.
Definition view.hpp:126
constexpr bool isSign(char c) noexcept
Definition atod.hpp:196
constexpr bool isDigit(char c) noexcept
Definition atod.hpp:191
std::unique_ptr< std::remove_pointer_t< locale_t >, LocaleDelete > LocalePtr
Definition atod.hpp:56
const char * strtodSlow(const char *beg, double &value)
Definition atod.hpp:58
void umul192(uint64_t hi, uint64_t lo, uint64_t significand, uint64_t &high, uint64_t &middle, uint64_t &low) noexcept
Definition atod.hpp:66
bool strtodFast(bool negative, uint64_t significand, int64_t exponent, double &value) noexcept
Definition atod.hpp:103
constexpr Power atodpow[]
Definition atodpow.hpp:40
const char * atod(View &view, double &value)
Definition atod.hpp:201
Definition atod.hpp:43
const char * atod(const char *str, double &value)
string to double conversion.
Definition atod.hpp:330
Definition atod.hpp:47
constexpr LocaleDelete() noexcept=default
Definition atodpow.hpp:35
uint64_t hi
Definition atodpow.hpp:36
uint64_t lo
Definition atodpow.hpp:37
#define likely(x)
Definition view.hpp:35
#define unlikely(x)
Definition view.hpp:36