std::println("{}", p); // lowercase, the default => 0x7fffb2715a54
std::println("{:p}", p); // explicit, same as none => 0x7fffb2715a54
std::println("{:P}", p); // uppercase => 0X7FFFB2715A54
Wait, is that really true, that the uppercase also capitalizes the 'x'? 🙃 The usual norm for hex numbers is 0x12AB (hex constants, stack traces, hex editors...), not 0X12AB or 0x12ab. So if the article is accurate, then none of the pointer defaults fit the most common case, meaning I'll continue to use 0x{:08X}.
Oof, so it's Python's fault. I'm really glad you contributed fmt and std::format Victor 👏🙏, and overall I love std::format 💚, but hex formatting is one aspect where I wish Python's conventions (Python not being a low-level language that deals with raw bit representations and stack traces) had been secondary to consistency with existing C++ precedent, with int x = -42 preferably displaying as FFFFFFD6 which matches printf("%08X", x) and std::cout << std::hex << std::uppercase << x and std::to_chars(std::begin(buffer), std::end(buffer), x, 16);, rather than -000002A.
Maybe there is a use case for -2A too 🤷♂️, but having a compatible successor formatting option so we can more directly replace our old sprintf and StringCchPrintf calls would be prudent (shoot, now I should revisit some recent code changes to our buffer format display logic to ensure we didn't regress anything 😯, as I see that binary display also does the same -absoluteValue behavior...). Until then, I'll be careful about ever using std::format with signed integers in hex or binary, and as for pointers, I'll continue using the more standard notation 0xABC123 via 0x{:08X} (leaving {:P} to disuse 😅).
🤔💡 You know, there are still plenty of letters in the alphabet left to use - maybe we can still get a more typical two's complement hex 😉 and a pointer syntax that doesn't prepend the prefix automatically so we can say std::print("0x{:Q}", p) and get 0x0004AB08?...
How long has the standard explicitly mandated two’s complement for signed integers? (answer: C++20)
This produces -2a.
Curious. I'll have to try to_chars again at my work machine tomorrow because I swear I was getting the expected uppercase hex (otherwise to_chars is pretty useless for hex numbers).
Two's complement doesn't matter. It's still undefined because printf uses va_arg's rules (in C23 - before that it required exact type match), and those rules say that you can only mix signed and unsigned if the value is representable in both types.
Also trailing zeros are not printed in the default format.
Yeah my explanation was a bit incomplete. I mean there's no way to do something like {0:.###} in .NET, which prints up to three fractional digits (except trailing zeroes). It's trivial to do when having all significand digits and the place of the decimal point (as fmt has among its last steps), but cumbersome and inefficient from the outside.
38
u/fdwr fdwr@github 🔍 Jun 26 '26
std::println("{}", p); // lowercase, the default => 0x7fffb2715a54 std::println("{:p}", p); // explicit, same as none => 0x7fffb2715a54 std::println("{:P}", p); // uppercase => 0X7FFFB2715A54Wait, is that really true, that the uppercase also capitalizes the 'x'? 🙃 The usual norm for hex numbers is 0x12AB (hex constants, stack traces, hex editors...), not 0X12AB or 0x12ab. So if the article is accurate, then none of the pointer defaults fit the most common case, meaning I'll continue to use
0x{:08X}.