dconv 2.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/eisellemire.hpp>
30#include <dconv/error.hpp>
31#include <dconv/probe.hpp>
32
33// C++.
34#include <type_traits>
35#include <string_view>
36#include <charconv>
37#include <string>
38#include <limits>
39
40// C.
41#include <cstring>
42#include <cstdint>
43#include <cstdlib>
44#include <cerrno>
45
46namespace dconv
47{
51 enum class AtodMode : uint32_t
52 {
53 Json = 0,
54 ParseNanAndInf = 1L << 0,
55 ParseRelaxedNumber = 1L << 1,
57 };
58
65 constexpr AtodMode operator& (AtodMode a, AtodMode b) noexcept
66 {
67 return AtodMode (static_cast<uint32_t> (a) & static_cast<uint32_t> (b));
68 }
69
76 constexpr AtodMode operator| (AtodMode a, AtodMode b) noexcept
77 {
78 return AtodMode (static_cast<uint32_t> (a) | static_cast<uint32_t> (b));
79 }
80
87 constexpr AtodMode operator^ (AtodMode a, AtodMode b) noexcept
88 {
89 return AtodMode (static_cast<uint32_t> (a) ^ static_cast<uint32_t> (b));
90 }
91
97 constexpr AtodMode operator~(AtodMode a) noexcept
98 {
99 return AtodMode (~static_cast<uint32_t> (a));
100 }
101
108 constexpr const AtodMode& operator&= (AtodMode& a, AtodMode b) noexcept
109 {
110 return a = a & b;
111 }
112
119 constexpr const AtodMode& operator|= (AtodMode& a, AtodMode b) noexcept
120 {
121 return a = a | b;
122 }
123
130 constexpr const AtodMode& operator^= (AtodMode& a, AtodMode b) noexcept
131 {
132 return a = a ^ b;
133 }
134
135 namespace details
136 {
137 inline constexpr bool isDigit (char c) noexcept
138 {
139 return static_cast<unsigned char> (c - '0') <= 9u;
140 }
141
142 inline constexpr bool isSign (char c) noexcept
143 {
144 return (c == '+') || (c == '-');
145 }
146
153 template <typename Probe>
154 inline bool readExponent (Probe& probe, int64_t& exponent)
155 {
156 exponent = 0;
157 if (!probe.getIf ('e') && !probe.getIf ('E'))
158 {
159 return true;
160 }
161
162 bool negExp = false;
163 if (isSign (probe.peek ()))
164 {
165 negExp = (probe.get () == '-');
166 }
167
168 if (DCONV_UNLIKELY (!isDigit (probe.peek ())))
169 {
170 return false;
171 }
172
173 exponent = probe.get () - '0';
174
175 while (isDigit (probe.peek ()))
176 {
177 int digit = probe.get () - '0';
178
179 if (DCONV_LIKELY (exponent <= ((std::numeric_limits<int>::max () - digit) / 10)))
180 {
181 exponent = (exponent * 10) + digit;
182 }
183 }
184
185 if (negExp)
186 {
187 exponent = -exponent;
188 }
189
190 return true;
191 }
192
204 template <typename Probe, bool Relaxed>
205 DCONV_COLD bool atodLong (Probe& probe, bool negative, uint64_t significand, int64_t& scale, bool fraction,
206 double& value, int pending = -1)
207 {
208 static constexpr int maxDigits = 800;
209 char text[maxDigits + 32];
210 char* const tail = std::to_chars (text, text + 20, significand).ptr;
211 char* cur = tail;
212 bool truncated = false;
213
214 if (pending >= 0)
215 {
216 *cur++ = static_cast<char> ('0' + pending);
217 ++scale;
218 }
219
220 if (!fraction)
221 {
222 while (isDigit (probe.peek ()))
223 {
224 char digit = static_cast<char> (probe.get ());
225 if (cur < text + maxDigits)
226 {
227 *cur++ = digit;
228 }
229 else
230 {
231 truncated |= (digit != '0');
232 }
233 ++scale;
234 }
235
236 if (probe.getIf ('.'))
237 {
238 if (DCONV_UNLIKELY (!Relaxed && !isDigit (probe.peek ())))
239 {
240 return false;
241 }
242 }
243 }
244
245 while (isDigit (probe.peek ()))
246 {
247 char digit = static_cast<char> (probe.get ());
248 if (cur < text + maxDigits)
249 {
250 *cur++ = digit;
251 }
252 else
253 {
254 truncated |= (digit != '0');
255 }
256 }
257
258 int64_t exponent = 0;
259 if constexpr (Relaxed && std::is_same_v<Probe, StringProbe>)
260 {
261 const char* mark = probe.tell ();
262 if (!readExponent (probe, exponent))
263 {
264 probe.seek (mark);
265 exponent = 0;
266 }
267 }
268 else
269 {
270 if (!readExponent (probe, exponent))
271 {
272 return false;
273 }
274 }
275
276 scale += exponent;
277 value = eisellemire::compute (significand, scale);
278 if (value != eisellemire::compute (significand + 1, scale))
279 {
280 if (truncated)
281 {
282 *cur++ = '1';
283 }
284 const int64_t power = scale - (cur - tail);
285 *cur++ = 'e';
286 *std::to_chars (cur, cur + 21, power).ptr = '\0';
287 int error = errno;
288 value = std::strtod (text, nullptr);
289 errno = error;
290 }
291
292 value = negative ? -value : value;
293 return true;
294 }
295
305 template <typename Probe, AtodMode Mode>
306 inline Errc atodRelaxed (Probe& probe, double& value, uint64_t& significand, int64_t& exponent, bool& negative)
307 {
308 constexpr bool special = (Mode & AtodMode::ParseNanAndInf) != AtodMode::Json;
309
310 significand = 0;
311 exponent = 0;
312 negative = probe.getIf ('-');
313 uint64_t u = 0;
314 int64_t scale = 0;
315 size_t digits = 0;
316
317 const bool zero = probe.getIf ('0');
318 if (zero)
319 {
320 while (probe.getIf ('0'))
321 {
322 }
323 }
324
325 if (isDigit (probe.peek ()))
326 {
327 digits = probe.readDigits (u, 19);
328 if (DCONV_UNLIKELY (isDigit (probe.peek ())))
329 {
330 significand = u;
331 const int pending = probe.get () - '0';
332 const int next = probe.peek ();
333 if (!isDigit (next) && (next != '.') && (next != 'e') && (next != 'E') &&
334 (u <= (std::numeric_limits<uint64_t>::max () - pending) / 10))
335 {
336 significand = (u * 10) + pending;
337 return Errc::NotDouble;
338 }
339 if (!atodLong<Probe, true> (probe, negative, u, exponent, false, value, pending))
340 {
341 return Errc::InvalidNumber;
342 }
343 return Errc::Success;
344 }
345 significand = u;
346 }
347 else if (!zero && (probe.peek () != '.'))
348 {
349 if (special && probe.getIfNoCase ('i') && probe.getIfNoCase ('n') && probe.getIfNoCase ('f'))
350 {
351 if constexpr (std::is_same_v<Probe, StringProbe>)
352 {
353 const char* mark = probe.tell ();
354 if (!(probe.getIfNoCase ('i') && probe.getIfNoCase ('n') && probe.getIfNoCase ('i') &&
355 probe.getIfNoCase ('t') && probe.getIfNoCase ('y')))
356 {
357 probe.seek (mark);
358 }
359 }
360 else
361 {
362 if (probe.getIfNoCase ('i'))
363 {
364 if (!(probe.getIfNoCase ('n') && probe.getIfNoCase ('i') && probe.getIfNoCase ('t') &&
365 probe.getIfNoCase ('y')))
366 {
367 return Errc::InvalidNumber;
368 }
369 }
370 }
371 value =
372 negative ? -std::numeric_limits<double>::infinity () : std::numeric_limits<double>::infinity ();
373 return Errc::Success;
374 }
375 else if (special && probe.getIfNoCase ('n') && probe.getIfNoCase ('a') && probe.getIfNoCase ('n'))
376 {
377 value = negative ? -std::numeric_limits<double>::quiet_NaN ()
378 : std::numeric_limits<double>::quiet_NaN ();
379 return Errc::Success;
380 }
381 return Errc::InvalidNumber;
382 }
383
384 bool real = false;
385 if (probe.getIf ('.'))
386 {
387 real = true;
388 if (DCONV_UNLIKELY (!isDigit (probe.peek ())) && !zero && (digits == 0))
389 {
390 return Errc::InvalidNumber;
391 }
392
393 if (digits == 0)
394 {
395 while (probe.getIf ('0'))
396 {
397 --scale;
398 }
399 }
400
401 size_t count = probe.readDigits (u, 19 - digits);
402 significand = u;
403 digits += count;
404 scale -= static_cast<int64_t> (count);
405 exponent = scale;
406
407 if (DCONV_UNLIKELY (isDigit (probe.peek ())))
408 {
409 if (!atodLong<Probe, true> (probe, negative, u, exponent, true, value))
410 {
411 return Errc::InvalidNumber;
412 }
413 return Errc::Success;
414 }
415 }
416
417 bool scientific = (probe.peek () == 'e') || (probe.peek () == 'E');
418 int64_t power = 0;
419
420 if constexpr (std::is_same_v<Probe, StringProbe>)
421 {
422 const char* mark = probe.tell ();
423 if (DCONV_UNLIKELY (!readExponent (probe, power)))
424 {
425 probe.seek (mark);
426 power = 0;
427 scientific = false;
428 }
429 }
430 else
431 {
432 if (!readExponent (probe, power))
433 {
434 return Errc::InvalidNumber;
435 }
436 }
437
438 real |= scientific;
439 if (!real)
440 {
441 return Errc::NotDouble;
442 }
443
444 exponent = scale + power;
445 value = eisellemire::compute (u, exponent);
446 value = negative ? -value : value;
447 return Errc::Success;
448 }
449
459 template <typename Probe, AtodMode Mode>
460 DCONV_COLD Errc atodRewind (Probe& probe, double& value, uint64_t& significand, int64_t& exponent,
461 bool& negative);
462
472 template <typename Probe, AtodMode Mode = AtodMode::General, bool Rewind = std::is_same_v<Probe, StringProbe>>
473 inline Errc atod (Probe& probe, double& value, uint64_t& significand, int64_t& exponent, bool& negative)
474 {
475 constexpr bool relaxed = (Mode & AtodMode::ParseRelaxedNumber) != AtodMode::Json;
476
477 if constexpr (relaxed && !Rewind)
478 {
479 return atodRelaxed<Probe, Mode> (probe, value, significand, exponent, negative);
480 }
481
482 const char* beg = nullptr;
483 if constexpr (Rewind)
484 {
485 beg = probe.tell ();
486 }
487
488 do
489 {
490 significand = 0;
491 exponent = 0;
492 negative = probe.getIf ('-');
493 uint64_t u = 0;
494 int64_t scale = 0;
495 size_t digits = 0;
496
497 if (probe.getIf ('0'))
498 {
499 if (DCONV_UNLIKELY (isDigit (probe.peek ())))
500 {
501 if constexpr (relaxed && Rewind)
502 {
503 break;
504 }
505 else
506 {
507 return Errc::InvalidNumber;
508 }
509 }
510 }
511 else if (DCONV_LIKELY (isDigit (probe.peek ())))
512 {
513 if constexpr (Rewind)
514 {
515 digits = probe.readDigits (u);
516 }
517 else
518 {
519 digits = probe.readDigits (u, 19);
520 if (DCONV_UNLIKELY (isDigit (probe.peek ())))
521 {
522 significand = u;
523 const int pending = probe.get () - '0';
524 const int next = probe.peek ();
525 if (!isDigit (next) && (next != '.') && (next != 'e') && (next != 'E') &&
526 (u <= (std::numeric_limits<uint64_t>::max () - pending) / 10))
527 {
528 significand = (u * 10) + pending;
529 return Errc::NotDouble;
530 }
531 if (!atodLong<Probe, relaxed> (probe, negative, u, exponent, false, value, pending))
532 {
533 return Errc::InvalidNumber;
534 }
535 return Errc::Success;
536 }
537 }
538 significand = u;
539 }
540 else if (((Mode & AtodMode::ParseNanAndInf) != AtodMode::Json) && probe.getIfNoCase ('i') &&
541 probe.getIfNoCase ('n') && probe.getIfNoCase ('f'))
542 {
543 if (probe.getIfNoCase ('i'))
544 {
545 if (!(probe.getIfNoCase ('n') && probe.getIfNoCase ('i') && probe.getIfNoCase ('t') &&
546 probe.getIfNoCase ('y')))
547 {
548 if constexpr (relaxed && Rewind)
549 {
550 break;
551 }
552 else
553 {
554 return Errc::InvalidNumber;
555 }
556 }
557 }
558 value =
559 negative ? -std::numeric_limits<double>::infinity () : std::numeric_limits<double>::infinity ();
560 return Errc::Success;
561 }
562 else if (((Mode & AtodMode::ParseNanAndInf) != AtodMode::Json) && probe.getIfNoCase ('n') &&
563 probe.getIfNoCase ('a') && probe.getIfNoCase ('n'))
564 {
565 value = negative ? -std::numeric_limits<double>::quiet_NaN ()
566 : std::numeric_limits<double>::quiet_NaN ();
567 return Errc::Success;
568 }
569 else
570 {
571 if constexpr (relaxed && Rewind)
572 {
573 break;
574 }
575 else
576 {
577 return Errc::InvalidNumber;
578 }
579 }
580
581 bool real = false;
582 if (probe.getIf ('.'))
583 {
584 real = true;
585 if (DCONV_UNLIKELY (!isDigit (probe.peek ())))
586 {
587 if constexpr (relaxed && Rewind)
588 {
589 break;
590 }
591 else
592 {
593 return Errc::InvalidNumber;
594 }
595 }
596
597 if (digits == 0)
598 {
599 while (probe.getIf ('0'))
600 {
601 --scale;
602 }
603 }
604
605 size_t count = probe.readDigits (u, Rewind ? std::numeric_limits<size_t>::max () : 19 - digits);
606 significand = u;
607 digits += count;
608 scale -= static_cast<int64_t> (count);
609 exponent = scale;
610
611 if constexpr (!Rewind)
612 {
613 if (DCONV_UNLIKELY (isDigit (probe.peek ())))
614 {
615 if (!atodLong<Probe, relaxed> (probe, negative, u, exponent, true, value))
616 {
617 return Errc::InvalidNumber;
618 }
619 return Errc::Success;
620 }
621 }
622 }
623
624 if constexpr (Rewind)
625 {
626 if (DCONV_UNLIKELY (digits > 19))
627 {
628 break;
629 }
630 }
631
632 real |= (probe.peek () == 'e') || (probe.peek () == 'E');
633 int64_t power = 0;
634
635 if (!readExponent (probe, power))
636 {
637 if constexpr (relaxed && Rewind)
638 {
639 break;
640 }
641 else
642 {
643 return Errc::InvalidNumber;
644 }
645 }
646
647 if (!real)
648 {
649 return Errc::NotDouble;
650 }
651
652 exponent = scale + power;
653 value = eisellemire::compute (u, exponent);
654 value = negative ? -value : value;
655 return Errc::Success;
656 }
657 while (false);
658
659 if constexpr (Rewind)
660 {
661 probe.seek (beg);
662 return atodRewind<Probe, Mode> (probe, value, significand, exponent, negative);
663 }
664 else
665 {
666 return Errc::InvalidNumber; // LCOV_EXCL_LINE
667 }
668 }
669
670 template <typename Probe, AtodMode Mode>
671 DCONV_COLD Errc atodRewind (Probe& probe, double& value, uint64_t& significand, int64_t& exponent,
672 bool& negative)
673 {
674 return atod<Probe, Mode, false> (probe, value, significand, exponent, negative);
675 }
676
683 template <typename Probe, AtodMode Mode = AtodMode::General>
684 inline bool atod (Probe& probe, double& value)
685 {
686 uint64_t significand;
687 int64_t exponent;
688 bool negative;
689 const Errc err = atod<Probe, Mode> (probe, value, significand, exponent, negative);
690 if (err == Errc::NotDouble)
691 {
692 value = negative ? -static_cast<double> (significand) : static_cast<double> (significand);
693 return true;
694 }
695 return err == Errc::Success;
696 }
697 }
698
709 template <AtodMode Mode = AtodMode::General>
710 inline int atod (StreamProbe& probe, double& value, uint64_t& significand, int64_t& exponent, bool& negative,
711 std::error_code& ec)
712 {
713 const Errc err = details::atod<StreamProbe, Mode> (probe, value, significand, exponent, negative);
714 if (err != Errc::Success)
715 {
716 ec = make_error_code (err);
717 return -1;
718 }
719 return 0;
720 }
721
728 template <AtodMode Mode = AtodMode::General>
729 inline bool atod (StreamProbe& probe, double& value)
730 {
731 return details::atod<StreamProbe, Mode> (probe, value);
732 }
733
740 template <AtodMode Mode = AtodMode::General>
741 inline bool atod (std::istream& in, double& value)
742 {
743 StreamProbe probe (in);
744 return details::atod<StreamProbe, Mode> (probe, value);
745 }
746
757 template <AtodMode Mode = AtodMode::General>
758 inline int atod (StringProbe& probe, double& value, uint64_t& significand, int64_t& exponent, bool& negative,
759 std::error_code& ec)
760 {
761 const Errc err = details::atod<StringProbe, Mode> (probe, value, significand, exponent, negative);
762 if (err != Errc::Success)
763 {
764 ec = make_error_code (err);
765 return -1;
766 }
767 return 0;
768 }
769
776 template <AtodMode Mode = AtodMode::General>
777 inline bool atod (StringProbe& probe, double& value)
778 {
779 return details::atod<StringProbe, Mode> (probe, value);
780 }
781
788 template <AtodMode Mode = AtodMode::General>
789 inline const char* atod (const char* str, double& value)
790 {
791 StringProbe probe (str);
792 return details::atod<StringProbe, Mode> (probe, value) ? probe.tell () : nullptr;
793 }
794
802 template <AtodMode Mode = AtodMode::General>
803 inline const char* atod (const char* str, size_t length, double& value)
804 {
805 StringProbe probe (str, length);
806 return details::atod<StringProbe, Mode> (probe, value) ? probe.tell () : nullptr;
807 }
808
816 template <AtodMode Mode = AtodMode::General>
817 inline const char* atod (const char* first, const char* last, double& value)
818 {
819 StringProbe probe (first, last);
820 return details::atod<StringProbe, Mode> (probe, value) ? probe.tell () : nullptr;
821 }
822
829 template <AtodMode Mode = AtodMode::General>
830 inline const char* atod (std::string_view str, double& value)
831 {
832 StringProbe probe (str.data (), str.size ());
833 return details::atod<StringProbe, Mode> (probe, value) ? probe.tell () : nullptr;
834 }
835
842 template <AtodMode Mode = AtodMode::General>
843 const char* atod (std::string&& str, double& value) = delete;
844}
845
846#endif
stream probe.
Definition probe.hpp:279
string probe.
Definition probe.hpp:47
ProbePos tell() const noexcept
get input position indicator.
Definition probe.hpp:230
bool readExponent(Probe &probe, int64_t &exponent)
read an optional exponent.
Definition atod.hpp:154
constexpr bool isSign(char c) noexcept
Definition atod.hpp:142
constexpr bool isDigit(char c) noexcept
Definition atod.hpp:137
DCONV_COLD bool atodLong(Probe &probe, bool negative, uint64_t significand, int64_t &scale, bool fraction, double &value, int pending=-1)
continue a conversion whose significand has more than 19 digits.
Definition atod.hpp:205
Errc atodRelaxed(Probe &probe, double &value, uint64_t &significand, int64_t &exponent, bool &negative)
string to double conversion in general format that stops on integers.
Definition atod.hpp:306
DCONV_COLD Errc atodRewind(Probe &probe, double &value, uint64_t &significand, int64_t &exponent, bool &negative)
parse again a number the fast path does not handle.
Definition atod.hpp:671
Errc atod(Probe &probe, double &value, uint64_t &significand, int64_t &exponent, bool &negative)
string to double conversion that stops on integers.
Definition atod.hpp:473
double compute(uint64_t significand, int64_t exponent) noexcept
convert double using fast path.
Definition eisellemire.hpp:49
Definition atod.hpp:47
Errc
conversion error codes.
Definition error.hpp:38
constexpr AtodMode operator&(AtodMode a, AtodMode b) noexcept
perform binary AND on AtodMode.
Definition atod.hpp:65
std::error_code make_error_code(Errc code) noexcept
create an std::error_code object.
Definition error.hpp:95
constexpr const AtodMode & operator|=(AtodMode &a, AtodMode b) noexcept
perform binary OR assignment on AtodMode.
Definition atod.hpp:119
constexpr AtodMode operator~(AtodMode a) noexcept
perform binary NOT on AtodMode.
Definition atod.hpp:97
constexpr const AtodMode & operator^=(AtodMode &a, AtodMode b) noexcept
perform binary XOR assignment on AtodMode.
Definition atod.hpp:130
constexpr AtodMode operator|(AtodMode a, AtodMode b) noexcept
perform binary OR on AtodMode.
Definition atod.hpp:76
AtodMode
string to double conversion mode.
Definition atod.hpp:52
constexpr const AtodMode & operator&=(AtodMode &a, AtodMode b) noexcept
perform binary AND assignment on AtodMode.
Definition atod.hpp:108
constexpr AtodMode operator^(AtodMode a, AtodMode b) noexcept
perform binary XOR on AtodMode.
Definition atod.hpp:87
int atod(StreamProbe &probe, double &value, uint64_t &significand, int64_t &exponent, bool &negative, std::error_code &ec)
stream to double conversion that stops on integers.
Definition atod.hpp:710
#define DCONV_LIKELY(x)
Definition probe.hpp:37
#define DCONV_UNLIKELY(x)
Definition probe.hpp:38
#define DCONV_COLD
Definition probe.hpp:39