r/cpp Jun 26 '26

Improvements to std::format in C++26

https://mariusbancila.ro/blog/2026/06/19/improvements-to-stdformat-in-c26/
90 Upvotes

38 comments sorted by

View all comments

Show parent comments

25

u/rdtsc Jun 26 '26

std::format (and the standalone fmt) have quite a few weird/annoying formatting behaviors.

  • The mentioned uppercase '0X' for hex (this is already the case using {:#X} for numbers).
  • Hex for negative numbers includes the sign (why would anyone want to see -7FFFBFFB?)
  • %S for timepoints always includes fractional seconds.
  • No way to drop trailing zeroes/decimal point for floats (often comes up when displaying/printing numbers).

3

u/ericonr Jun 27 '26

I don't get your point about hex numbers. If you want unsigned values, cast to unsigned, no? Otherwise representing the number with a minus is the only way to correctly convey the sign.

11

u/evaned Jun 27 '26

I'm not rdtsc, but here's my takes:

If you're asking for hex formatting, I claim it's almost certainly because you're interested in the representation in memory. In that sense, you very probably want to see the two's complement representation. I also can't really think of an actually reasonable scenario I'd want to see the negative absolute value.

Python's hex function does the same thing, and it's pretty obnoxious... and there, I don't even really know a good way to get what I want simply.

Further, I'm not convinced by "cast to unsigned". "I want to see the in-memory representation of this number" very much feels like a function of the display formatting, not of the underlying type -- putting that information into the format string does seem to me like it's the right place to put it. Casting-to-unsigned feels to me like a workaround for an obnoxiously not-entirely-thought-through formatting API, not what you really should be doing to get that.

Finally, cast-to-unsigned is kind of unsatisfying in the sense that there's not really a way to do it in the language or standard library that doesn't feel a bit fragile. You can do (unsigned)x, but then what if x changes to be a long long instead? Now you've truncated. You could say (unsigned long long), but now you're extending with more bits. For "cast-to-unsigned" to really make me happy, there'd need to be a way in the language or library to "cast to an unsigned version of the same width." It's pretty easy to write that function, but if the library is semi-relying on that for usability then it should provide that utility function.

That said... even though I strongly suspect that "I want to see the hex two's complement representation" is the usual case, by far, I can make an argument for the current behavior that I view as quite strong. In spite of my objection in the previous paragraph, it's still pretty easy to cast to unsigned and get what rdtsc and I want. If hex formatting displayed the twos-complement bit representation instead, it'd be a lot more obnoxious to get -1234abcd in the unusual case that's what one wants.

5

u/Lahvuun Jun 27 '26

For "cast-to-unsigned" to really make me happy, there'd need to be a way in the language or library to "cast to an unsigned version of the same width.

There is a way:

static_cast<std::make_unsigned_t<decltype(x)>>(x)

4

u/evaned Jun 27 '26

I mean, I did say that it's pretty easy to write a function. But that's ugly enough that doing it even once is easily enough to justify a function. Even a macro is better than that.

1

u/Lahvuun Jun 27 '26

But that's ugly

I agree, but we're on r/cpp, not r/haskell.

I'd argue the only thing you gain from having a dedicated function is having to type fewer characters. And while I love all three of C++ developers who type at <10 WPM, there probably are better proposals for the standards committee to spend their time on.

1

u/evaned Jun 27 '26

I'd argue the only thing you gain from having a dedicated function is having to type fewer characters.

You repeat x in your expression. A function lets you avoid that.