Skip to content

[libc++] LWG2381: Inconsistency in parsing floating point numbers #77948

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
May 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion libcxx/docs/Status/Cxx23Issues.csv
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@
`3555 <https://wg21.link/LWG3555>`__,"``{transform,elements}_view::iterator::iterator_concept`` should consider const-qualification of the underlying range","June 2021","","","|ranges|"
"","","","","",""
`2191 <https://wg21.link/LWG2191>`__,"Incorrect specification of ``match_results(match_results&&)``","October 2021","|Nothing To Do|",""
`2381 <https://wg21.link/LWG2381>`__,"Inconsistency in parsing floating point numbers","October 2021","",""
`2381 <https://wg21.link/LWG2381>`__,"Inconsistency in parsing floating point numbers","October 2021","|Complete|","19.0"
`2762 <https://wg21.link/LWG2762>`__,"``unique_ptr operator*()`` should be ``noexcept``","October 2021","",""
`3121 <https://wg21.link/LWG3121>`__,"``tuple`` constructor constraints for ``UTypes&&...`` overloads","October 2021","",""
`3123 <https://wg21.link/LWG3123>`__,"``duration`` constructor from representation shouldn't be effectively non-throwing","October 2021","","","|chrono|"
Expand Down
53 changes: 37 additions & 16 deletions libcxx/include/locale
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,11 @@ struct _LIBCPP_EXPORTED_FROM_ABI __num_get_base {
static const int __num_get_buf_sz = 40;

static int __get_base(ios_base&);
static const char __src[33];
static const char __src[33]; // "0123456789abcdefABCDEFxX+-pPiInN"
// count of leading characters in __src used for parsing integers ("012..X+-")
static const size_t __int_chr_cnt = 26;
// count of leading characters in __src used for parsing floating-point values ("012..-pP")
static const size_t __fp_chr_cnt = 28;
};

_LIBCPP_EXPORTED_FROM_ABI void
Expand Down Expand Up @@ -431,7 +435,7 @@ private:
template <typename _Tp>
const _Tp* __do_widen_p(ios_base& __iob, _Tp* __atoms) const {
locale __loc = __iob.getloc();
use_facet<ctype<_Tp> >(__loc).widen(__src, __src + 26, __atoms);
use_facet<ctype<_Tp> >(__loc).widen(__src, __src + __int_chr_cnt, __atoms);
return __atoms;
}

Expand All @@ -447,7 +451,7 @@ private:
template <class _CharT>
string __num_get<_CharT>::__stage2_int_prep(ios_base& __iob, _CharT* __atoms, _CharT& __thousands_sep) {
locale __loc = __iob.getloc();
std::use_facet<ctype<_CharT> >(__loc).widen(__src, __src + 26, __atoms);
std::use_facet<ctype<_CharT> >(__loc).widen(__src, __src + __int_chr_cnt, __atoms);
const numpunct<_CharT>& __np = std::use_facet<numpunct<_CharT> >(__loc);
__thousands_sep = __np.thousands_sep();
return __np.grouping();
Expand All @@ -458,7 +462,7 @@ template <class _CharT>
string __num_get<_CharT>::__stage2_float_prep(
ios_base& __iob, _CharT* __atoms, _CharT& __decimal_point, _CharT& __thousands_sep) {
locale __loc = __iob.getloc();
std::use_facet<ctype<_CharT> >(__loc).widen(__src, __src + 32, __atoms);
std::use_facet<ctype<_CharT> >(__loc).widen(__src, __src + __fp_chr_cnt, __atoms);
const numpunct<_CharT>& __np = std::use_facet<numpunct<_CharT> >(__loc);
__decimal_point = __np.decimal_point();
__thousands_sep = __np.thousands_sep();
Expand Down Expand Up @@ -490,7 +494,7 @@ __num_get<_CharT>::__stage2_int_loop(_CharT __ct, int __base, char* __a, char*&
}
return 0;
}
ptrdiff_t __f = std::find(__atoms, __atoms + 26, __ct) - __atoms;
ptrdiff_t __f = std::find(__atoms, __atoms + __int_chr_cnt, __ct) - __atoms;
if (__f >= 24)
return -1;
switch (__base) {
Expand Down Expand Up @@ -546,8 +550,8 @@ int __num_get<_CharT>::__stage2_float_loop(
}
return 0;
}
ptrdiff_t __f = std::find(__atoms, __atoms + 32, __ct) - __atoms;
if (__f >= 32)
ptrdiff_t __f = std::find(__atoms, __atoms + __num_get_base::__fp_chr_cnt, __ct) - __atoms;
if (__f >= static_cast<ptrdiff_t>(__num_get_base::__fp_chr_cnt))
return -1;
char __x = __src[__f];
if (__x == '-' || __x == '+') {
Expand Down Expand Up @@ -846,7 +850,7 @@ _InputIterator num_get<_CharT, _InputIterator>::__do_get_signed(
int __base = this->__get_base(__iob);
// Stage 2
char_type __thousands_sep;
const int __atoms_size = 26;
const int __atoms_size = __num_get_base::__int_chr_cnt;
#ifdef _LIBCPP_ABI_OPTIMIZED_LOCALE_NUM_GET
char_type __atoms1[__atoms_size];
const char_type* __atoms = this->__do_widen(__iob, __atoms1);
Expand Down Expand Up @@ -895,7 +899,7 @@ _InputIterator num_get<_CharT, _InputIterator>::__do_get_unsigned(
int __base = this->__get_base(__iob);
// Stage 2
char_type __thousands_sep;
const int __atoms_size = 26;
const int __atoms_size = __num_get_base::__int_chr_cnt;
#ifdef _LIBCPP_ABI_OPTIMIZED_LOCALE_NUM_GET
char_type __atoms1[__atoms_size];
const char_type* __atoms = this->__do_widen(__iob, __atoms1);
Expand Down Expand Up @@ -942,7 +946,7 @@ _InputIterator num_get<_CharT, _InputIterator>::__do_get_floating_point(
iter_type __b, iter_type __e, ios_base& __iob, ios_base::iostate& __err, _Fp& __v) const {
// Stage 1, nothing to do
// Stage 2
char_type __atoms[32];
char_type __atoms[__num_get_base::__fp_chr_cnt];
char_type __decimal_point;
char_type __thousands_sep;
string __grouping = this->__stage2_float_prep(__iob, __atoms, __decimal_point, __thousands_sep);
Expand All @@ -951,10 +955,11 @@ _InputIterator num_get<_CharT, _InputIterator>::__do_get_floating_point(
char* __a = &__buf[0];
char* __a_end = __a;
unsigned __g[__num_get_base::__num_get_buf_sz];
unsigned* __g_end = __g;
unsigned __dc = 0;
bool __in_units = true;
char __exp = 'E';
unsigned* __g_end = __g;
unsigned __dc = 0;
bool __in_units = true;
char __exp = 'E';
bool __is_leading_parsed = false;
for (; __b != __e; ++__b) {
if (__a_end == __a + __buf.size()) {
size_t __tmp = __buf.size();
Expand All @@ -977,6 +982,21 @@ _InputIterator num_get<_CharT, _InputIterator>::__do_get_floating_point(
__dc,
__atoms))
break;

// the leading character excluding the sign must be a decimal digit
if (!__is_leading_parsed) {
if (__a_end - __a >= 1 && __a[0] != '-' && __a[0] != '+') {
if ('0' <= __a[0] && __a[0] <= '9')
__is_leading_parsed = true;
else
break;
} else if (__a_end - __a >= 2 && (__a[0] == '-' || __a[0] == '+')) {
if ('0' <= __a[1] && __a[1] <= '9')
__is_leading_parsed = true;
else
break;
}
}
}
if (__grouping.size() != 0 && __in_units && __g_end - __g < __num_get_base::__num_get_buf_sz)
*__g_end++ = __dc;
Expand All @@ -996,10 +1016,11 @@ _InputIterator num_get<_CharT, _InputIterator>::do_get(
// Stage 1
int __base = 16;
// Stage 2
char_type __atoms[26];
char_type __atoms[__num_get_base::__int_chr_cnt];
char_type __thousands_sep = char_type();
string __grouping;
std::use_facet<ctype<_CharT> >(__iob.getloc()).widen(__num_get_base::__src, __num_get_base::__src + 26, __atoms);
std::use_facet<ctype<_CharT> >(__iob.getloc())
.widen(__num_get_base::__src, __num_get_base::__src + __num_get_base::__int_chr_cnt, __atoms);
string __buf;
__buf.resize(__buf.capacity());
char* __a = &__buf[0];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@
//
//===----------------------------------------------------------------------===//

// The fix for LWG2381 (https://github.com/llvm/llvm-project/pull/77948) changed
// behavior of FP parsing, while Apple back-deployment targets remain broken due
// to the dylib.
// UNSUPPORTED: using-built-library-before-llvm-19

// <locale>

// class num_get<charT, InputIterator>
Expand Down Expand Up @@ -116,9 +121,9 @@ int main(int, char**)
f.get(cpp17_input_iterator<const char*>(str),
cpp17_input_iterator<const char*>(str+sizeof(str)),
ios, err, v);
assert(base(iter) == str+sizeof(str)-1);
assert(err == ios.goodbit);
assert(v == INFINITY);
assert(base(iter) == str);
assert(err == ios.failbit);
assert(v == 0.0);
}
{
const char str[] = "INF";
Expand All @@ -128,9 +133,9 @@ int main(int, char**)
f.get(cpp17_input_iterator<const char*>(str),
cpp17_input_iterator<const char*>(str+sizeof(str)),
ios, err, v);
assert(base(iter) == str+sizeof(str)-1);
assert(err == ios.goodbit);
assert(v == INFINITY);
assert(base(iter) == str);
assert(err == ios.failbit);
assert(v == 0.0);
}
{
const char str[] = "-inf";
Expand All @@ -140,9 +145,9 @@ int main(int, char**)
f.get(cpp17_input_iterator<const char*>(str),
cpp17_input_iterator<const char*>(str+sizeof(str)),
ios, err, v);
assert(base(iter) == str+sizeof(str)-1);
assert(err == ios.goodbit);
assert(v == -INFINITY);
assert(base(iter) == str + 1);
assert(err == ios.failbit);
assert(v == 0.0);
}
{
const char str[] = "-INF";
Expand All @@ -152,9 +157,9 @@ int main(int, char**)
f.get(cpp17_input_iterator<const char*>(str),
cpp17_input_iterator<const char*>(str+sizeof(str)),
ios, err, v);
assert(base(iter) == str+sizeof(str)-1);
assert(err == ios.goodbit);
assert(v == -INFINITY);
assert(base(iter) == str + 1);
assert(err == ios.failbit);
assert(v == 0.0);
}
{
const char str[] = "nan";
Expand All @@ -164,9 +169,9 @@ int main(int, char**)
f.get(cpp17_input_iterator<const char*>(str),
cpp17_input_iterator<const char*>(str+sizeof(str)),
ios, err, v);
assert(base(iter) == str+sizeof(str)-1);
assert(err == ios.goodbit);
assert(std::isnan(v));
assert(base(iter) == str);
assert(err == ios.failbit);
assert(v == 0.0);
}
{
const char str[] = "NAN";
Expand All @@ -176,9 +181,129 @@ int main(int, char**)
f.get(cpp17_input_iterator<const char*>(str),
cpp17_input_iterator<const char*>(str+sizeof(str)),
ios, err, v);
assert(base(iter) == str+sizeof(str)-1);
assert(err == ios.goodbit);
assert(std::isnan(v));
assert(base(iter) == str);
assert(err == ios.failbit);
assert(v == 0.0);
}
{
const char str[] = "p00";
std::hex(ios);
std::ios_base::iostate err = ios.goodbit;
cpp17_input_iterator<const char*> iter = f.get(
cpp17_input_iterator<const char*>(str), cpp17_input_iterator<const char*>(str + sizeof(str)), ios, err, v);
assert(base(iter) == str);
assert(err == ios.failbit);
assert(v == 0.0);
}
{
const char str[] = "P00";
std::hex(ios);
std::ios_base::iostate err = ios.goodbit;
cpp17_input_iterator<const char*> iter = f.get(
cpp17_input_iterator<const char*>(str), cpp17_input_iterator<const char*>(str + sizeof(str)), ios, err, v);
assert(base(iter) == str);
assert(err == ios.failbit);
assert(v == 0.0);
}
{
const char str[] = "+p00";
std::hex(ios);
std::ios_base::iostate err = ios.goodbit;
cpp17_input_iterator<const char*> iter = f.get(
cpp17_input_iterator<const char*>(str), cpp17_input_iterator<const char*>(str + sizeof(str)), ios, err, v);
assert(base(iter) == str + 1);
assert(err == ios.failbit);
assert(v == 0.0);
}
{
const char str[] = "+P00";
std::hex(ios);
std::ios_base::iostate err = ios.goodbit;
cpp17_input_iterator<const char*> iter = f.get(
cpp17_input_iterator<const char*>(str), cpp17_input_iterator<const char*>(str + sizeof(str)), ios, err, v);
assert(base(iter) == str + 1);
assert(err == ios.failbit);
assert(v == 0.0);
}
{
const char str[] = "-p00";
std::hex(ios);
std::ios_base::iostate err = ios.goodbit;
cpp17_input_iterator<const char*> iter = f.get(
cpp17_input_iterator<const char*>(str), cpp17_input_iterator<const char*>(str + sizeof(str)), ios, err, v);
assert(base(iter) == str + 1);
assert(err == ios.failbit);
assert(v == 0.0);
}
{
const char str[] = "-P00";
std::hex(ios);
std::ios_base::iostate err = ios.goodbit;
cpp17_input_iterator<const char*> iter = f.get(
cpp17_input_iterator<const char*>(str), cpp17_input_iterator<const char*>(str + sizeof(str)), ios, err, v);
assert(base(iter) == str + 1);
assert(err == ios.failbit);
assert(v == 0.0);
}
{
const char str[] = "e00";
std::hex(ios);
std::ios_base::iostate err = ios.goodbit;
cpp17_input_iterator<const char*> iter = f.get(
cpp17_input_iterator<const char*>(str), cpp17_input_iterator<const char*>(str + sizeof(str)), ios, err, v);
assert(base(iter) == str);
assert(err == ios.failbit);
assert(v == 0.0);
}
{
const char str[] = "E00";
std::hex(ios);
std::ios_base::iostate err = ios.goodbit;
cpp17_input_iterator<const char*> iter = f.get(
cpp17_input_iterator<const char*>(str), cpp17_input_iterator<const char*>(str + sizeof(str)), ios, err, v);
assert(base(iter) == str);
assert(err == ios.failbit);
assert(v == 0.0);
}
{
const char str[] = "+e00";
std::hex(ios);
std::ios_base::iostate err = ios.goodbit;
cpp17_input_iterator<const char*> iter = f.get(
cpp17_input_iterator<const char*>(str), cpp17_input_iterator<const char*>(str + sizeof(str)), ios, err, v);
assert(base(iter) == str + 1);
assert(err == ios.failbit);
assert(v == 0.0);
}
{
const char str[] = "+E00";
std::hex(ios);
std::ios_base::iostate err = ios.goodbit;
cpp17_input_iterator<const char*> iter = f.get(
cpp17_input_iterator<const char*>(str), cpp17_input_iterator<const char*>(str + sizeof(str)), ios, err, v);
assert(base(iter) == str + 1);
assert(err == ios.failbit);
assert(v == 0.0);
}
{
const char str[] = "-e00";
std::hex(ios);
std::ios_base::iostate err = ios.goodbit;
cpp17_input_iterator<const char*> iter = f.get(
cpp17_input_iterator<const char*>(str), cpp17_input_iterator<const char*>(str + sizeof(str)), ios, err, v);
assert(base(iter) == str + 1);
assert(err == ios.failbit);
assert(v == 0.0);
}
{
const char str[] = "-E00";
std::hex(ios);
std::ios_base::iostate err = ios.goodbit;
cpp17_input_iterator<const char*> iter = f.get(
cpp17_input_iterator<const char*>(str), cpp17_input_iterator<const char*>(str + sizeof(str)), ios, err, v);
assert(base(iter) == str + 1);
assert(err == ios.failbit);
assert(v == 0.0);
}
{
v = -1;
Expand Down
Loading
Loading