I can specify the number of digits like printf("%08x", ...)
. But I'd like to automatically adjust the number of digits according to the data type, (eg, long
(64 bits) should automatically have 64/4=16 digits). Is there a way to do so? Thanks.
How to
printf(“%0x”)
with the number of digits determined from the type?
1) Find the bit width
Use sizeof, CHAR_BIT
to find the bit width sizeof(x)*CHAR_BIT
1 or see below alternative.
2) Find the nibble width
(bit_width + 3)/4
for the nibble width - number of hexadecimal characters.
3) Pass the width
Use "*"
to pass in the width and print using the widest type, uintmax_t
.
#include <float.h>
#include <limits.h>
#include <stdio.h>
#define PRINTF_XW(x) printf("%0*jx\n", (int)(sizeof(x)*CHAR_BIT+3)/4, (uintmax_t) (x))
int main(void) {
unsigned char uc = 1;
unsigned short us = 2;
unsigned u = 3;
unsigned long ul = 4;
unsigned long long ull = 5;
uintmax_t uj = 6;
PRINTF_XW(uc);
PRINTF_XW(us);
PRINTF_XW(u);
PRINTF_XW(ul);
PRINTF_XW(ull);
PRINTF_XW(uj);
return 0;
}
Sample output, may differ amongst platforms.
01
0002
00000003
0000000000000004
0000000000000005
0000000000000006
Alternative
Another approach would use _Generic
- tis a bit more work - and more rewards.
See unsigned long long uf:42;
example below.
#include <float.h>
#include <limits.h>
#include <stdint.h>
int hexwidth(uintmax_t u) {
int count = 3;
while (u) {
u >>= 1;
count++;
}
return count/4;
}
// Default case useful for other unsigned types wider than `unsigned`
#define HEXWIDTH(s) _Generic((s), \
unsigned char: hexwidth(UCHAR_MAX), \
unsigned short: hexwidth(USHRT_MAX), \
unsigned : hexwidth(UINT_MAX), \
unsigned long: hexwidth(ULONG_MAX), \
unsigned long long: hexwidth(ULLONG_MAX), \
default: hexwidth((s)*0u - 1u) \
)
#define PRINTF_XW(x) printf("%0*jx\n", HEXWIDTH(x), (uintmax_t) (x))
With language extensions that allow wider than unsigned
fields - works nicely too.
int main(void) {
struct {
unsigned long long uf:42;
} s1 = {7};
struct {
unsigned long long uf:51;
} s2 = {8};
PRINTF_XW(s1.uf);
PRINTF_XW(s2.uf);
}
Output
00000000007 <-- 11 digits
0000000000008 <-- 13 digits
Macro-magic could replace hexwidth(uintmax_t u)
as follows with a compile time calculation:
#define NW3(x) (((x) > 0xFu) ? 2 : 1)
#define NW4(x) (((x) > 0xFFu) ? NW3((x)>>8)+2 : NW3(x))
#define NW5(x) (((x) > 0xFFFFu) ? NW4((x)>>16)+4 : NW4(x))
#define NW6(x) (((x) > 0xFFFFFFFFu) ? NW5((x)>>32)+8 : NW5(x))
#define NW(x) (((x) > 0xFFFFFFFFFFFFFFFFu) ? NW6((((x)+0llu)>>32)>>32)+16 : NW6((x)+0llu))
// Default case useful for all unsigned types as wide/wider than `unsigned`
#define HEXWIDTH(s) _Generic((s), \
unsigned char: NW(UCHAR_MAX), \
unsigned short: NW(USHRT_MAX), \
default: NW((s)*0u - 1u) \
)
1 This works well when there is no padding in the type - common amongst standard unsigned integer types.
You should probably look into C's <inttypes.h>
#include <inttypes.h>
unit8_t value = 0x34;
printf("The value formatted for 8 bits is %" PRIx8 ".", value);
Note the PRIx8
is a string, which correctly will format for "printf" "hexadecimal" "eight bits".
Note that the lack of commas between the strings are not a mistake. This is a formatting technique that uses a feature in C called "automatic string concatenation", such that the three strings will get slapped together. The PRIx8
is just a convenience which means the correct formatting for an 8 bit hexadecimal value.
To get the similar output formatted for 32 bits, you would use PRIx32
.
For a brief overview of the inttypes.h
output formats, you can look at Good introduction to <inttypes.h>
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.