TraceMaker
C++ API of the engine
Loading...
Searching...
No Matches
parse_double.hpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-3.0-or-later
2#pragma once
3#include <cerrno>
4#include <cmath>
5#include <cstdlib>
6#include <optional>
7#include <string>
8#include <string_view>
9
10#if defined(__APPLE__)
11#include <xlocale.h>
12#else
13#include <charconv>
14#include <locale.h>
15#endif
16
17namespace tmk {
18
19namespace detail {
20
21// strtod_l with the "C" locale, restricted to the grammar of std::from_chars(chars_format::general): strtod also
22// takes leading whitespace, '+' and hex, which from_chars does not. Correctly rounded on glibc and Apple's libc, so
23// it gives the same double as from_chars; tests/test_units.cpp checks that wherever from_chars exists.
24inline std::optional<double> parse_double_strtod(std::string_view s) {
25 if (s.empty()) return std::nullopt;
26 const std::size_t sign = s[0] == '-' ? 1 : 0;
27 if (sign == s.size()) return std::nullopt;
28 const char c = s[sign];
29 if (!((c >= '0' && c <= '9') || c == '.' || c == 'i' || c == 'I' || c == 'n' || c == 'N')) return std::nullopt;
30 if (c == '0' && sign + 1 < s.size() && (s[sign + 1] == 'x' || s[sign + 1] == 'X')) return std::nullopt;
31 static const locale_t c_locale = newlocale(LC_ALL_MASK, "C", locale_t{});
32 const std::string buf(s); // strtod needs a terminated string
33 char* end = nullptr;
34 errno = 0;
35 const double v = strtod_l(buf.c_str(), &end, c_locale);
36 if (end != buf.c_str() + buf.size()) return std::nullopt;
37 // strtod also sets ERANGE for a subnormal result, which from_chars returns; only overflow and underflow to zero
38 // are result_out_of_range there.
39 if (errno == ERANGE && (v == 0.0 || v == HUGE_VAL || v == -HUGE_VAL)) return std::nullopt;
40 return v;
41}
42
43#if !defined(__APPLE__)
44inline std::optional<double> parse_double_from_chars(std::string_view s) {
45 double v = 0;
46 const auto r = std::from_chars(s.data(), s.data() + s.size(), v);
47 if (r.ec != std::errc{} || r.ptr != s.data() + s.size()) return std::nullopt;
48 return v;
49}
50#endif
51
52} // namespace detail
53
54// Parses all of `s` as a decimal floating-point number, independent of the C locale; nullopt if it is not one
55// or a character is left over. Apple's libc++ marks floating-point std::from_chars unavailable before macOS 26,
56// so Apple platforms use the strtod_l path (D135).
57inline std::optional<double> parse_double(std::string_view s) {
58#if defined(__APPLE__)
60#else
62#endif
63}
64
65} // namespace tmk
std::optional< double > parse_double_strtod(std::string_view s)
strtod_l with the "C" locale, restricted to the grammar of std::from_chars(chars_format::general): st...
std::optional< double > parse_double_from_chars(std::string_view s)
Definition advice.hpp:44
std::optional< double > parse_double(std::string_view s)
Parses all of s as a decimal floating-point number, independent of the C locale; nullopt if it is not...