aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonathan Wakely <jwakely@redhat.com>2024-01-20 00:44:12 +0000
committerJonathan Wakely <jwakely@redhat.com>2024-01-21 13:31:28 +0000
commita57439d61937925cec48df6166b2a805ae7054d5 (patch)
tree922370fc0d69e39a1444da53ac866e994d4fc824
parent29f931e39f2be86b454a8264b1cd42e4ca3cdcd7 (diff)
downloadgcc-a57439d61937925cec48df6166b2a805ae7054d5.zip
gcc-a57439d61937925cec48df6166b2a805ae7054d5.tar.gz
gcc-a57439d61937925cec48df6166b2a805ae7054d5.tar.bz2
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.
-rw-r--r--libstdc++-v3/include/std/format51
-rw-r--r--libstdc++-v3/testsuite/std/format/functions/format.cc6
2 files changed, 41 insertions, 16 deletions
diff --git a/libstdc++-v3/include/std/format b/libstdc++-v3/include/std/format
index f4d9151..0eca8b5 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 30c5fc2..a27fbe7 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