Skip to content

<format>: Alternate form general floating-point can mishandle width #5011

Description

Reported to me by Attila Feher. https://godbolt.org/z/djh5KxjW6

#include <format>
#include <iostream>
using namespace std;

int main() {
    std::cout << "Expected: [1.e-37]\n";
    std::cout << format("  Actual: [{:#6.0g}]\n", 1.234e-37);
}

libstdc++ and libc++ behave as expected, but MSVC prints:

Expected: [1.e-37]
  Actual: [ 1.e-37]

🕵️ Analysis

I debugged into this, and although I'm not absolutely certain of the root cause yet, this line appears to be involved:

_Zeroes_to_append = _Extra_precision + _Precision - _Digits;

_Zeroes_to_append is computed as -1 because _Extra_precision and _Precision are both 0, and _Digits is 1.

We've correctly computed _Width to be 6, but the negative _Zeroes_to_append damages it, which appears to be the proximate cause of the bug:

_Width += _Zeroes_to_append;

Later, we use _Zeroes_to_append in a clamped manner (i.e. negative values are treated as zero):

STL/stl/inc/format

Lines 2328 to 2330 in 926d458

for (; _Zeroes_to_append > 0; --_Zeroes_to_append) {
*_Out++ = '0';
}

However, we need to completely understand the problem and all possible codepaths before developing a fix (i.e. we can't just clamp it to zero and call it a day). Note that the chars_format::general codepath further adjusts _Zeroes_to_append:

STL/stl/inc/format

Lines 2278 to 2289 in 926d458

_Zeroes_to_append = _Extra_precision + _Precision - _Digits;
// Leading zeroes are not significant if we used fixed point notation.
if (_Exponent_start == _Result.ptr && _STD abs(_Value) < 1.0 && _Value != 0.0) {
for (auto _It = _Buffer_start; _It < _Result.ptr; ++_It) {
if (*_It == '0') {
++_Zeroes_to_append;
} else if (*_It != '.') {
break;
}
}
}

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingfixedSomething works now, yay!formatC++20/23 format

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions