sprintf for unsigned _int64
Asked Answered
L

4

22

I am having following code. output of second %d in sprintf is always shown as zero. I think i am specifying wrong specifiers. Can any one help me in getting write string with right values. And this has to achieved in posix standard. Thanks for inputs

void main() {
    unsigned _int64 dbFileSize = 99;
    unsigned _int64 fileSize = 100;
    char buf[128];
    memset(buf, 0x00, 128);
    sprintf(buf, "\nOD DB File Size = %d bytes \t XML file size = %d bytes", fileSize, dbFileSize);
    printf("The string is %s ", buf);
    }

Output:

The string is
OD DB File Size = 100 bytes      XML file size = 0 bytes 
Lodovico answered 28/2, 2011 at 10:37 Comment(3)
The C99 printf prefix for long long is ll, thus %lld, but your use of _int64 makes me fear you are using a windows extension and IIRC, it was using a non standard prefix.Extraterrestrial
There is no _int64 in the POSIX standard.Espy
See also #3344Belomancy
O
19

I don't know what POSIX has to say about this, but this is nicely handled by core C99:

#include <stdio.h>
#include <inttypes.h>

int main(void) {
    uint64_t dbFileSize = 99;
    uint64_t fileSize = 100;
    char buf[128];
    memset(buf, 0x00, 128);
    sprintf( buf, "\nOD DB File Size = %" PRIu64 " bytes \t"
                  " XML file size = %" PRIu64 " bytes\n"
                  , fileSize, dbFileSize );
    printf( "The string is %s\n", buf );
}

If your compiler isn't C99 compliant, get a different compiler. (Yes, I'm looking at you, Visual Studio.)

PS: If you are worried about portability, don't use %lld. That's for long long, but there are no guarantees that long long actually is the same as _int64 (POSIX) or int64_t (C99).

Edit: Mea culpa - I more or less brainlessly "search & replace"d the _int64 with int64_t without really looking at what I am doing. Thanks for the comments pointing out that it's uint64_t, not unsigned int64_t. Corrected.

Ollie answered 28/2, 2011 at 10:44 Comment(11)
You mean PRIu64 for unsigned?Serialize
You can portably use %lld if you cast the argument to (long long int) (or just (long long)) because no integer can be bigger than that.Affairs
I think your code doesn't even compile, no? You can't prefix a typedef name by unsigned. The correct C99 typedef here would be uint64_t. Also using memset instead of just initializing the buf correctly is bad style, char buf[128] = { 0 } would do it.Tergum
@Jim Balter: Implementations are at liberty to define additional types in the intX_t form, including but not limited to types bigger than 64 bit. There is no guarantee that intmax_t and long long are identical.Ollie
@Rup: @Jens Gustedt: You are right, of course. See updated answer.Ollie
@Ollie type specifiers are listed in 6.7.2. intmax_t is defined in stdint.h. How might a type larger than long long be defined there?Affairs
@Jim Balter: Yes, 6.7.2 lists the required types. But note that 7.1.3 reserves identifiers for the implementation, and both 7.8 and 7.18 allow for additional, optional types. An implementation might define _Long128, int128_t and uint128_t (plus the necessary print / scan macros in <inttypes.h>). But that is neither here nor there: Assuming that long long is 64bit is exactly the type of assumption that made generations of C programs break on major updates.Ollie
@Ollie 6.7.2 lists the types, period. It includes typedef names, which of course include uint128_t etc. But the standard says what a legal typedef is. Regardless of what may have been intended, I don't believe it's possible for a conforming impl to have an int type bigger than unsigned long long. As for the assumption you mention, I didn't make it -- I said "if you cast" -- casting a uint64_t to (unsigned long long) only assumes the latter has at least 64 bits. And it doesn't even assume that -- it only assumes that the value being cast can fit. In practice, that's portable.Affairs
@Ollie P.S. Even if an implementation can, per the Standard, provide a uint64_t while at the same time making unsigned long long only 32 bits (which is what it would take to make %ull and a cast to (unsigned long long) fail in this case), that would fall under "quality of implementation" ... it would be a low quality one. There have been implementations of that sort that justify themselves via sophistic language lawyering (like saying its allowed for UD to set off nuclear war) but they fail in the marketplace.Affairs
"If you are worried about portability" and C99 macros are not available, could use uint64_t u64; sprintf( buf, "%llu", (unsigned long long) u64); as unsigned long long is at least 64 bits.Jaco
@chux: If you're worried about portability, and C99 macros are not available, get a proper compiler. ;-) (Yes, three years after this answer, three years after C++11 officially embraced those macros added to the C standard fifteen years ago, MSVC is still non-conforming. Put that in your pipe and smoke it.)Ollie
I
19

You need to use %I64u with Visual C++.

However, on most C/C++ compiler, 64 bit integer is long long. Therefore, adopt to using long long and use %llu.

Intoxicant answered 28/2, 2011 at 10:41 Comment(2)
Not on Linux. On LP64, a 64-bit integer is a long!Raddie
What is a long long on Linux?Ecumenism
O
19

I don't know what POSIX has to say about this, but this is nicely handled by core C99:

#include <stdio.h>
#include <inttypes.h>

int main(void) {
    uint64_t dbFileSize = 99;
    uint64_t fileSize = 100;
    char buf[128];
    memset(buf, 0x00, 128);
    sprintf( buf, "\nOD DB File Size = %" PRIu64 " bytes \t"
                  " XML file size = %" PRIu64 " bytes\n"
                  , fileSize, dbFileSize );
    printf( "The string is %s\n", buf );
}

If your compiler isn't C99 compliant, get a different compiler. (Yes, I'm looking at you, Visual Studio.)

PS: If you are worried about portability, don't use %lld. That's for long long, but there are no guarantees that long long actually is the same as _int64 (POSIX) or int64_t (C99).

Edit: Mea culpa - I more or less brainlessly "search & replace"d the _int64 with int64_t without really looking at what I am doing. Thanks for the comments pointing out that it's uint64_t, not unsigned int64_t. Corrected.

Ollie answered 28/2, 2011 at 10:44 Comment(11)
You mean PRIu64 for unsigned?Serialize
You can portably use %lld if you cast the argument to (long long int) (or just (long long)) because no integer can be bigger than that.Affairs
I think your code doesn't even compile, no? You can't prefix a typedef name by unsigned. The correct C99 typedef here would be uint64_t. Also using memset instead of just initializing the buf correctly is bad style, char buf[128] = { 0 } would do it.Tergum
@Jim Balter: Implementations are at liberty to define additional types in the intX_t form, including but not limited to types bigger than 64 bit. There is no guarantee that intmax_t and long long are identical.Ollie
@Rup: @Jens Gustedt: You are right, of course. See updated answer.Ollie
@Ollie type specifiers are listed in 6.7.2. intmax_t is defined in stdint.h. How might a type larger than long long be defined there?Affairs
@Jim Balter: Yes, 6.7.2 lists the required types. But note that 7.1.3 reserves identifiers for the implementation, and both 7.8 and 7.18 allow for additional, optional types. An implementation might define _Long128, int128_t and uint128_t (plus the necessary print / scan macros in <inttypes.h>). But that is neither here nor there: Assuming that long long is 64bit is exactly the type of assumption that made generations of C programs break on major updates.Ollie
@Ollie 6.7.2 lists the types, period. It includes typedef names, which of course include uint128_t etc. But the standard says what a legal typedef is. Regardless of what may have been intended, I don't believe it's possible for a conforming impl to have an int type bigger than unsigned long long. As for the assumption you mention, I didn't make it -- I said "if you cast" -- casting a uint64_t to (unsigned long long) only assumes the latter has at least 64 bits. And it doesn't even assume that -- it only assumes that the value being cast can fit. In practice, that's portable.Affairs
@Ollie P.S. Even if an implementation can, per the Standard, provide a uint64_t while at the same time making unsigned long long only 32 bits (which is what it would take to make %ull and a cast to (unsigned long long) fail in this case), that would fall under "quality of implementation" ... it would be a low quality one. There have been implementations of that sort that justify themselves via sophistic language lawyering (like saying its allowed for UD to set off nuclear war) but they fail in the marketplace.Affairs
"If you are worried about portability" and C99 macros are not available, could use uint64_t u64; sprintf( buf, "%llu", (unsigned long long) u64); as unsigned long long is at least 64 bits.Jaco
@chux: If you're worried about portability, and C99 macros are not available, get a proper compiler. ;-) (Yes, three years after this answer, three years after C++11 officially embraced those macros added to the C standard fifteen years ago, MSVC is still non-conforming. Put that in your pipe and smoke it.)Ollie
M
11

If you are looking for a portable solution, then use printf macros from <inttypes.h>. You may need to define __STDC_FORMAT_MACROS to make these available in C++.

Muir answered 28/2, 2011 at 10:49 Comment(2)
Yes you may have to. The C standard says that the macro is needed for C++, and the C++0x draft says that you don't have to use it. Wonder which standard the header conforms to. :-)Qualifier
The more current one (C++0x). I'm wondering if the C committee will again make a new version of the C standard one year after the C++ standard comes out, just to spite them. :-\Ollie
C
0

I would use sprintf_s with %dz and uint64_t, and promptly discover that I'm an idiot when I try it and find that %dz, the format specifier for an unsigned int, will work for uint32_t, which is good news, but it won't work for uint64_t. While I think Keith Thompson has already answered this adequately with regards to using PRIu64, one change I would make would be to use the buffer overrun safe versions sprintf_s and printf_s, another would be to (direct) initialize the char when it is declared, as suggested by Jens Gustedt;

#include <stdio.h>
#include <inttypes.h>

uint64_t dbFileSize = 99;
uint64_t fileSize = 100;
char buf[128]{ 0 };
sprintf_s(buf, "\nOD DB File Size = %" PRIu64 " bytes \t XML file size = %" PRIu64 " bytes\n", fileSize, dbFileSize);
printf_s("The string is %s ", buf);
Caldera answered 17/1, 2022 at 21:45 Comment(1)
Don't say things like "but then I'm just a beginner, what do I know?". There's no reason to apologize for inexperience. Answer to the best of your ability, show references backing up your answer and explain why it's the best answer. I removed that, so now flesh out the answer.Inkwell

© 2022 - 2024 — McMap. All rights reserved.