Fmt: unexpected narrowing integer cast when using (unsigned) long long on sprintf with %ld/u

Created on 24 Sep 2020  路  7Comments  路  Source: fmtlib/fmt

Hi, using sprintf to format a long long value narrows it to plain long if %ld is used. All other methods (%lld, %d and of course format) format as long long.
Sure, using %ld with long long is UB and C sprintf also narrows, but fmt has full type information and could do better, as it already does with %d.
Some examples below

fmt::sprintf("%llu", ULLONG_MAX) -> 18446744073709551615
fmt::sprintf("%lu", ULLONG_MAX) -> 4294967295
fmt::sprintf("%u", ULLONG_MAX) -> 18446744073709551615
fmt::format("{}", ULLONG_MAX) -> 18446744073709551615

fmt::sprintf("%lld", LLONG_MIN) -> -9223372036854775808
fmt::sprintf("%ld", LLONG_MIN) -> 0
fmt::sprintf("%d", LLONG_MIN) -> -9223372036854775808
fmt::format("{}", LLONG_MIN) -> -9223372036854775808

fmt::sprintf("%lld", LLONG_MAX) -> 9223372036854775807
fmt::sprintf("%ld", LLONG_MAX) -> -1
fmt::sprintf("%d", LLONG_MAX) -> 9223372036854775807
fmt::format("{}", LLONG_MAX) -> 9223372036854775807

wontfix

All 7 comments

perhaps is something MSVC specific. I'll check.
Thanks

It's platform specific. On windows long is 32bit and long long 64, while on linux both are 64bit, so a cast doesn't change values.
In printf.h line 547 there is

    case 'l':
      if (t == 'l') {
        ++it;
        t = it != end ? *it : 0;
        convert_arg<long long>(arg, t);
      } else {
        convert_arg<long>(arg, t);
      }
      break;

On linux the convert_arg<long>(arg, t); is a no-op between long and long long, while on windows it truncates to 32bit

The conversion is intentional for consistency with printf: https://godbolt.org/z/5oq713. If you want to avoid it, use fmt::print.

well, yes, but fmt knows the parameter real type and could do better than printf, like it does for plain %u: https://godbolt.org/z/Wbhb69
Imho this is one of great features of fmt: the ability of furnish a printf like interface but avoiding a whole class of C-related problems and errors
Obviously the format interface is way better (BTW, thanks for making it a standard!), but for legacy code fmt::sprintf is wonderful.

PS sorry, I've used the wrong account for commenting

Here, compatibility with printf is more important. fmt::printf is only there for migrating legacy code.

ok, I understand, thanks.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

sab24 picture sab24  路  21Comments

jk-jeon picture jk-jeon  路  15Comments

jiri picture jiri  路  22Comments

newnon picture newnon  路  23Comments

Gachapen picture Gachapen  路  14Comments