From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 2181) id 1B0453858D3C; Sun, 21 Jan 2024 13:35:15 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 1B0453858D3C DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1705844115; bh=dyLlR/ej9QtRwXz8lTdK+TCSgVPqpve3gmmkjltoQKQ=; h=From:To:Subject:Date:From; b=Ibfm7Vmemta8VQwtk0+7ev2rSPGB0c6WpZNiGMjB6GvvOWikGrf4LQlOTunrv5y09 CkyRtcUmLltIJPjirLwqM52hwo+zgb3YCIE22xh3l4UiTNSsylafqDiFiKEkDsdRo/ Y1xvCb0kDVtXjHsw6N9GVOhYm/N661BMwc5J3xPQ= MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Type: text/plain; charset="utf-8" From: Jonathan Wakely To: gcc-cvs@gcc.gnu.org, libstdc++-cvs@gcc.gnu.org Subject: [gcc r14-8315] libstdc++: Fix std::format floating-point alternate forms [PR113512] X-Act-Checkin: gcc X-Git-Author: Jonathan Wakely X-Git-Refname: refs/heads/master X-Git-Oldrev: 29f931e39f2be86b454a8264b1cd42e4ca3cdcd7 X-Git-Newrev: a57439d61937925cec48df6166b2a805ae7054d5 Message-Id: <20240121133515.1B0453858D3C@sourceware.org> Date: Sun, 21 Jan 2024 13:35:15 +0000 (GMT) List-Id: https://gcc.gnu.org/g:a57439d61937925cec48df6166b2a805ae7054d5 commit r14-8315-ga57439d61937925cec48df6166b2a805ae7054d5 Author: Jonathan Wakely Date: Sat Jan 20 00:44:12 2024 +0000 libstdc++: Fix std::format floating-point alternate forms [PR113512] The logic for handling '#' forms was ... not good. The count of significant figures just counted digits, instead of ignoring leading zeros. And when moving the result from the stack buffer to a dynamic string the exponent could get lost in some cases. libstdc++-v3/ChangeLog: PR libstdc++/113512 * include/std/format (__formatter_fp::format): Fix logic for alternate forms. * testsuite/std/format/functions/format.cc: Check buggy cases of alternate forms with g presentation type. Diff: --- libstdc++-v3/include/std/format | 51 +++++++++++++++------- .../testsuite/std/format/functions/format.cc | 6 +++ 2 files changed, 41 insertions(+), 16 deletions(-) diff --git a/libstdc++-v3/include/std/format b/libstdc++-v3/include/std/format index f4d91517656..0eca8b58bfa 100644 --- a/libstdc++-v3/include/std/format +++ b/libstdc++-v3/include/std/format @@ -1623,6 +1623,7 @@ namespace __format *__p = std::toupper(*__p); } + bool __have_sign = true; // Add sign for non-negative values. if (!__builtin_signbit(__v)) { @@ -1630,56 +1631,73 @@ namespace __format *--__start = '+'; else if (_M_spec._M_sign == _Sign_space) *--__start = ' '; + else + __have_sign = false; } string_view __narrow_str(__start, __res.ptr - __start); - // Use alternate form. + // Use alternate form. Ensure decimal point is always present, + // and add trailing zeros (up to precision) for g and G forms. if (_M_spec._M_alt && __builtin_isfinite(__v)) { string_view __s = __narrow_str; - size_t __z = 0; - size_t __p; - size_t __d = __s.find('.'); - size_t __sigfigs; - if (__d != __s.npos) + size_t __sigfigs; // Number of significant figures. + size_t __z = 0; // Number of trailing zeros to add. + size_t __p; // Position of the exponent character (if any). + size_t __d = __s.find('.'); // Position of decimal point. + if (__d != __s.npos) // Found decimal point. { __p = __s.find(__expc, __d + 1); if (__p == __s.npos) __p = __s.size(); - __sigfigs = __p - 1; + + // If presentation type is g or G we might need to add zeros. + if (__trailing_zeros) + { + // Find number of digits after first significant figure. + if (__s[__have_sign] != '0') + // A string like "D.D" or "-D.DDD" + __sigfigs = __p - __have_sign - 1; + else + // A string like "0.D" or "-0.0DD". + // Safe to assume there is a non-zero digit, because + // otherwise there would be no decimal point. + __sigfigs = __p - __s.find_first_not_of('0', __d + 1); + } } - else + else // No decimal point, we need to insert one. { - __p = __s.find(__expc); + __p = __s.find(__expc); // Find the exponent, if present. if (__p == __s.npos) __p = __s.size(); __d = __p; // Position where '.' should be inserted. - __sigfigs = __d; + __sigfigs = __d - __have_sign; } if (__trailing_zeros && __prec != 0) { - if (!__format::__is_xdigit(__s[0])) - --__sigfigs; - __z = __prec - __sigfigs; // Number of zeros to insert. + // For g and G presentation types std::to_chars produces + // no more than prec significant figures. Insert this many + // zeros so the result has exactly prec significant figures. + __z = __prec - __sigfigs; } - if (size_t __extras = int(__d == __p) + __z) + if (size_t __extras = int(__d == __p) + __z) // How many to add. { if (__dynbuf.empty() && __extras <= size_t(__end - __res.ptr)) { + // The stack buffer is large enough for the result. // Move exponent to make space for extra chars. __builtin_memmove(__start + __p + __extras, __start + __p, __s.size() - __p); - if (__d == __p) __start[__p++] = '.'; __builtin_memset(__start + __p, '0', __z); __narrow_str = {__s.data(), __s.size() + __extras}; } - else + else // Need to switch to the dynamic buffer. { __dynbuf.reserve(__s.size() + __extras); if (__dynbuf.empty()) @@ -1689,6 +1707,7 @@ namespace __format __dynbuf += '.'; if (__z) __dynbuf.append(__z, '0'); + __dynbuf.append(__s.substr(__p)); } else { diff --git a/libstdc++-v3/testsuite/std/format/functions/format.cc b/libstdc++-v3/testsuite/std/format/functions/format.cc index 30c5fc22237..a27fbe74631 100644 --- a/libstdc++-v3/testsuite/std/format/functions/format.cc +++ b/libstdc++-v3/testsuite/std/format/functions/format.cc @@ -181,6 +181,12 @@ test_alternate_forms() // PR libstdc++/108046 s = std::format("{0:#.0} {0:#.1} {0:#.0g}", 10.0); VERIFY( s == "1.e+01 1.e+01 1.e+01" ); + + // PR libstdc++/113512 + s = std::format("{:#.3g}", 0.025); + VERIFY( s == "0.0250" ); + s = std::format("{:#07.3g}", 0.02); + VERIFY( s == "00.0200" ); } void