1 // std::from_chars implementation for floating-point types -*- C++ -*-
3 // Copyright (C) 2020-2021 Free Software Foundation, Inc.
5 // This file is part of the GNU ISO C++ Library. This library is free
6 // software; you can redistribute it and/or modify it under the
7 // terms of the GNU General Public License as published by the
8 // Free Software Foundation; either version 3, or (at your option)
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // Under Section 7 of GPL version 3, you are granted additional
17 // permissions described in the GCC Runtime Library Exception, version
18 // 3.1, as published by the Free Software Foundation.
20 // You should have received a copy of the GNU General Public License and
21 // a copy of the GCC Runtime Library Exception along with this program;
22 // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
23 // <http://www.gnu.org/licenses/>.
27 // 23.2.9 Primitive numeric input conversion [utility.from.chars]
30 // Prefer to use std::pmr::string if possible, which requires the cxx11 ABI.
31 #define _GLIBCXX_USE_CXX11_ABI 1
35 #include <memory_resource>
42 #include <bits/functexcept.h>
43 #if _GLIBCXX_HAVE_XLOCALE_H
47 #ifdef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT
48 #ifndef __LONG_DOUBLE_IBM128__
49 #error "floating_from_chars.cc must be compiled with -mabi=ibmlongdouble"
51 // strtold for __ieee128
52 extern "C" __ieee128
__strtoieee128(const char*, char**);
55 #if _GLIBCXX_HAVE_USELOCALE
56 namespace std
_GLIBCXX_VISIBILITY(default)
58 _GLIBCXX_BEGIN_NAMESPACE_VERSION
62 // A memory resource with a static buffer that can be used for small
63 // allocations. At most one allocation using the freestore can be done
64 // if the static buffer is insufficient. The callers below only require
65 // a single allocation, so there's no need for anything more complex.
66 struct buffer_resource
: pmr::memory_resource
68 ~buffer_resource() { if (m_ptr
) operator delete(m_ptr
, m_bytes
); }
71 do_allocate(size_t bytes
, size_t alignment
[[maybe_unused
]]) override
73 // Allocate from the buffer if it will fit.
74 if (m_bytes
< sizeof(m_buf
) && (m_bytes
+ bytes
) <= sizeof(m_buf
))
75 return m_buf
+ std::__exchange(m_bytes
, m_bytes
+ bytes
);
77 __glibcxx_assert(m_ptr
== nullptr);
78 __glibcxx_assert(alignment
!= 1);
80 m_ptr
= operator new(bytes
);
86 do_deallocate(void*, size_t, size_t) noexcept override
87 { /* like pmr::monotonic_buffer_resource, do nothing here */ }
90 do_is_equal(const pmr::memory_resource
& other
) const noexcept override
91 { return &other
== this; }
93 static constexpr int guaranteed_capacity() { return sizeof(m_buf
); }
98 void* m_ptr
= nullptr;
101 #if _GLIBCXX_USE_CXX11_ABI
102 using buffered_string
= std::pmr::string
;
104 using buffered_string
= std::string
;
107 inline bool valid_fmt(chars_format fmt
)
109 return fmt
!= chars_format
{}
110 && ((fmt
& chars_format::general
) == fmt
111 || (fmt
& chars_format::hex
) == fmt
);
114 constexpr char hex_digits
[] = "abcdefABCDEF0123456789";
115 constexpr auto dec_digits
= hex_digits
+ 12;
117 // Find initial portion of [first, last) containing a floating-point number.
118 // The string `digits` is either `dec_digits` or `hex_digits`
119 // and `exp` is 'e' or 'p' or '\0'.
121 find_end_of_float(const char* first
, const char* last
, const char* digits
,
124 while (first
< last
&& strchr(digits
, *first
) != nullptr)
126 if (first
< last
&& *first
== '.')
129 while (first
< last
&& strchr(digits
, *first
))
132 if (first
< last
&& exp
!= 0 && std::tolower((unsigned char)*first
) == exp
)
135 if (first
< last
&& (*first
== '-' || *first
== '+'))
137 while (first
< last
&& strchr(dec_digits
, *first
) != nullptr)
143 // Determine the prefix of [first, last) that matches the pattern
144 // corresponding to `fmt`.
145 // Returns a NTBS containing the pattern, using `buf` to allocate
146 // additional storage if needed.
147 // Returns a nullptr if a valid pattern is not present.
149 pattern(const char* const first
, const char* last
,
150 chars_format
& fmt
, buffered_string
& buf
)
152 // fmt has the value of one of the enumerators of chars_format.
153 __glibcxx_assert(valid_fmt(fmt
));
157 if (first
== last
|| *first
== '+') [[unlikely
]]
160 const int neg
= (*first
== '-');
162 if (std::memchr("iInN", (unsigned char)first
[neg
], 4))
164 ptrdiff_t len
= last
- first
;
168 // possible infinity or NaN, let strtod decide
169 if (first
[neg
] == 'i' || first
[neg
] == 'I')
171 // Need at most 9 chars for "-INFINITY", ignore anything after it.
172 len
= std::min(len
, ptrdiff_t(neg
+ 8));
174 else if (len
> (neg
+ 3) && first
[neg
+ 3] == '(')
176 // Look for end of "NAN(n-char-sequence)"
177 if (void* p
= std::memchr(const_cast<char*>(first
)+4, ')', len
-4))
178 len
= static_cast<char*>(p
) + 1 - first
;
179 #ifndef __cpp_exceptions
180 if (len
> buffer_resource::guaranteed_capacity())
182 // The character sequence is too large for the buffer.
183 // Allocation failure could terminate the process,
184 // so just return an error via the fmt parameter.
185 fmt
= chars_format
{};
190 else // Only need 4 chars for "-NAN"
193 buf
.assign(first
, 0, len
);
194 // prevent make_result correcting for "0x"
195 fmt
= chars_format::general
;
202 // Assign [first,last) to a std::string to get a NTBS that can be used
203 // with strspn, strtod etc.
204 // If the string would be longer than the fixed buffer inside the
205 // buffer_resource type use find_end_of_float to try to reduce how
206 // much memory is needed, to reduce the chance of std::bad_alloc.
208 if (fmt
== chars_format::hex
)
212 if ((last
- first
+ 2) > buffer_resource::guaranteed_capacity())
214 last
= find_end_of_float(first
+ neg
, last
, digits
, 'p');
215 #ifndef __cpp_exceptions
216 if ((last
- first
+ 2) > buffer_resource::guaranteed_capacity())
218 // The character sequence is still too large for the buffer.
219 // Allocation failure could terminate the process,
220 // so just return an error via the fmt parameter.
221 fmt
= chars_format
{};
228 buf
.append(first
+ neg
, last
);
229 ptr
= buf
.data() + neg
+ 2;
235 if ((last
- first
) > buffer_resource::guaranteed_capacity())
237 last
= find_end_of_float(first
+ neg
, last
, digits
,
238 "e"[fmt
== chars_format::fixed
]);
239 #ifndef __cpp_exceptions
240 if ((last
- first
) > buffer_resource::guaranteed_capacity())
242 // The character sequence is still too large for the buffer.
243 // Allocation failure could terminate the process,
244 // so just return an error via the fmt parameter.
245 fmt
= chars_format
{};
250 buf
.assign(first
, last
);
251 ptr
= buf
.data() + neg
;
254 // "A non-empty sequence of decimal digits" or
255 // "A non-empty sequence of hexadecimal digits"
256 size_t len
= std::strspn(ptr
, digits
);
257 // "possibly containing a radix character,"
260 const size_t len2
= std::strspn(ptr
+ len
+ 1, digits
);
262 ptr
+= len
+ 1 + len2
;
266 else if (len
== 0) [[unlikely
]]
271 if (fmt
== chars_format::fixed
)
273 // Truncate the string to stop strtod parsing past this point.
276 else if (fmt
== chars_format::scientific
)
278 // Check for required exponent part which starts with 'e' or 'E'
279 if (*ptr
!= 'e' && *ptr
!= 'E')
281 // then an optional plus or minus sign
282 const int sign
= (ptr
[1] == '-' || ptr
[1] == '+');
283 // then a nonempty sequence of decimal digits
284 if (!std::memchr(dec_digits
, (unsigned char)ptr
[1+sign
], 10))
287 else if (fmt
== chars_format::general
)
289 if (*ptr
== 'x' || *ptr
== 'X')
296 // Convert the NTBS `str` to a floating-point value of type `T`.
297 // If `str` cannot be converted, `value` is unchanged and `0` is returned.
298 // Otherwise, let N be the number of characters consumed from `str`.
299 // On success `value` is set to the converted value and N is returned.
300 // If the converted value is out of range, `value` is unchanged and
304 from_chars_impl(const char* str
, T
& value
, errc
& ec
) noexcept
306 if (locale_t loc
= ::newlocale(LC_ALL_MASK
, "C", (locale_t
)0)) [[likely
]]
308 locale_t orig
= ::uselocale(loc
);
310 #if _GLIBCXX_USE_C99_FENV_TR1 && defined(FE_TONEAREST)
311 const int rounding
= std::fegetround();
312 if (rounding
!= FE_TONEAREST
)
313 std::fesetround(FE_TONEAREST
);
316 const int save_errno
= errno
;
320 #if _GLIBCXX_USE_C99_STDLIB
321 if constexpr (is_same_v
<T
, float>)
322 tmpval
= std::strtof(str
, &endptr
);
323 else if constexpr (is_same_v
<T
, double>)
324 tmpval
= std::strtod(str
, &endptr
);
325 else if constexpr (is_same_v
<T
, long double>)
326 tmpval
= std::strtold(str
, &endptr
);
327 # ifdef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT
328 else if constexpr (is_same_v
<T
, __ieee128
>)
329 tmpval
= __strtoieee128(str
, &endptr
);
332 tmpval
= std::strtod(str
, &endptr
);
334 const int conv_errno
= std::__exchange(errno
, save_errno
);
336 #if _GLIBCXX_USE_C99_FENV_TR1 && defined(FE_TONEAREST)
337 if (rounding
!= FE_TONEAREST
)
338 std::fesetround(rounding
);
344 const ptrdiff_t n
= endptr
- str
;
345 if (conv_errno
== ERANGE
) [[unlikely
]]
347 if (__builtin_isinf(tmpval
)) // overflow
348 ec
= errc::result_out_of_range
;
349 else // underflow (LWG 3081 wants to set value = tmpval here)
350 ec
= errc::result_out_of_range
;
359 else if (errno
== ENOMEM
)
360 ec
= errc::not_enough_memory
;
365 inline from_chars_result
366 make_result(const char* str
, ptrdiff_t n
, chars_format fmt
, errc ec
) noexcept
368 from_chars_result result
= { str
, ec
};
371 if (fmt
== chars_format::hex
)
372 n
-= 2; // correct for the "0x" inserted into the pattern
375 else if (fmt
== chars_format
{}) [[unlikely
]]
377 // FIXME: the standard does not allow this result.
378 ec
= errc::not_enough_memory
;
383 #if ! _GLIBCXX_USE_CXX11_ABI
385 reserve_string(std::string
& s
) noexcept
389 s
.reserve(buffer_resource::guaranteed_capacity());
391 __catch (const std::bad_alloc
&)
401 // FIXME: This should be reimplemented so it doesn't use strtod and newlocale.
402 // That will avoid the need for any memory allocation, meaning that the
403 // non-conforming errc::not_enough_memory result cannot happen.
406 from_chars(const char* first
, const char* last
, float& value
,
407 chars_format fmt
) noexcept
409 errc ec
= errc::invalid_argument
;
410 #if _GLIBCXX_USE_CXX11_ABI
412 pmr::string
buf(&mr
);
415 if (!reserve_string(buf
))
416 return make_result(first
, 0, {}, ec
);
421 if (const char* pat
= pattern(first
, last
, fmt
, buf
)) [[likely
]]
422 len
= from_chars_impl(pat
, value
, ec
);
424 __catch (const std::bad_alloc
&)
426 fmt
= chars_format
{};
428 return make_result(first
, len
, fmt
, ec
);
432 from_chars(const char* first
, const char* last
, double& value
,
433 chars_format fmt
) noexcept
435 errc ec
= errc::invalid_argument
;
436 #if _GLIBCXX_USE_CXX11_ABI
438 pmr::string
buf(&mr
);
441 if (!reserve_string(buf
))
442 return make_result(first
, 0, {}, ec
);
447 if (const char* pat
= pattern(first
, last
, fmt
, buf
)) [[likely
]]
448 len
= from_chars_impl(pat
, value
, ec
);
450 __catch (const std::bad_alloc
&)
452 fmt
= chars_format
{};
454 return make_result(first
, len
, fmt
, ec
);
458 from_chars(const char* first
, const char* last
, long double& value
,
459 chars_format fmt
) noexcept
461 errc ec
= errc::invalid_argument
;
462 #if _GLIBCXX_USE_CXX11_ABI
464 pmr::string
buf(&mr
);
467 if (!reserve_string(buf
))
468 return make_result(first
, 0, {}, ec
);
473 if (const char* pat
= pattern(first
, last
, fmt
, buf
)) [[likely
]]
474 len
= from_chars_impl(pat
, value
, ec
);
476 __catch (const std::bad_alloc
&)
478 fmt
= chars_format
{};
480 return make_result(first
, len
, fmt
, ec
);
483 #ifdef _GLIBCXX_LONG_DOUBLE_COMPAT
484 // Make std::from_chars for 64-bit long double an alias for the overload
486 extern "C" from_chars_result
487 _ZSt10from_charsPKcS0_ReSt12chars_format(const char* first
, const char* last
,
489 chars_format fmt
) noexcept
490 __attribute__((alias ("_ZSt10from_charsPKcS0_RdSt12chars_format")));
493 #ifdef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT
495 from_chars(const char* first
, const char* last
, __ieee128
& value
,
496 chars_format fmt
) noexcept
499 pmr::string
buf(&mr
);
501 errc ec
= errc::invalid_argument
;
504 if (const char* pat
= pattern(first
, last
, fmt
, buf
)) [[likely
]]
505 len
= from_chars_impl(pat
, value
, ec
);
507 __catch (const std::bad_alloc
&)
509 fmt
= chars_format
{};
511 return make_result(first
, len
, fmt
, ec
);
515 _GLIBCXX_END_NAMESPACE_VERSION
517 #endif // _GLIBCXX_HAVE_USELOCALE