Last active
July 23, 2026 23:58
-
-
Save dgoguerra/7194777 to your computer and use it in GitHub Desktop.
Format a quantity in bytes into a human readable string (C)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <stdio.h> | |
| #include <stdlib.h> // atoll | |
| #include <stdint.h> // uint64_t | |
| #include <inttypes.h> // PRIu64 | |
| static const char *humanSize(uint64_t bytes) | |
| { | |
| char *suffix[] = {"B", "KB", "MB", "GB", "TB"}; | |
| char length = sizeof(suffix) / sizeof(suffix[0]); | |
| int i = 0; | |
| double dblBytes = bytes; | |
| if (bytes > 1024) { | |
| for (i = 0; (bytes / 1024) > 0 && i<length-1; i++, bytes /= 1024) | |
| dblBytes = bytes / 1024.0; | |
| } | |
| static char output[200]; | |
| sprintf(output, "%.02lf %s", dblBytes, suffix[i]); | |
| return output; | |
| } | |
| int main(int argc, char **argv) | |
| { | |
| if (argc == 1) { | |
| fprintf(stderr, "Usage: %s <bytes>\n", *argv); | |
| return 1; | |
| } | |
| uint64_t bytes = atoll(argv[1]); | |
| printf("%" PRIu64 " Bytes: %s\n", bytes, humanSize(bytes)); | |
| return 0; | |
| } |
Here is a way to do it without functions or scary static memory and in constant time.
#define PriByte "%.02lf %s"
#define PriByte__value(b) ( \
(unsigned long long)b >= ((((1ull << 10) << 10) << 10) << 10) ? (double)b / (double)((((1ull << 10) << 10) << 10) << 10) : \
(unsigned long long)b >= (((1ull << 10) << 10) << 10) ? (double)b / (double)(((1ull << 10) << 10) << 10) : \
(unsigned long long)b >= ((1ull << 10) << 10) ? (double)b / (double)((1ull << 10) << 10) : \
(unsigned long long)b >= (1ull << 10) ? (double)b / (double)(1ull << 10) : \
(double)b \
)
#define PriByte__unit(b) ( \
(unsigned long long)b >= ((((1ull << 10) << 10) << 10) << 10) ? "TiB" : \
(unsigned long long)b >= (((1ull << 10) << 10) << 10) ? "GiB" : \
(unsigned long long)b >= ((1ull << 10) << 10) ? "MiB" : \
(unsigned long long)b >= (1ull << 10) ? "KiB" : \
"B" \
)
#define PriByteArg(b) PriByte__value(b), PriByte__unit(b)Usage: printf("My size is "PriByte, PriByteArg(1024));
Here are some tests comparing it with the humanSize function from OP, check it out:
CLICK TO EXPAND.
size_t b = 2;
for (int i = 0; i < 100; ++i) {
b += b / 2;
printf("1 Macro "PriByte"\n", PriByteArg(b));
printf("2 Func %s\n", humanSize(b));
}
printf("---Next test---\n");
b = 1;
for (int i = 0; i < 64; ++i) {
printf("1 Macro "PriByte"\n", PriByteArg(b));
printf("2 Func %s\n", humanSize(b));
b = b << 1;
}1 Macro 3.00 B
2 Func 3.00 B
1 Macro 4.00 B
2 Func 4.00 B
1 Macro 6.00 B
2 Func 6.00 B
1 Macro 9.00 B
2 Func 9.00 B
1 Macro 13.00 B
2 Func 13.00 B
1 Macro 19.00 B
2 Func 19.00 B
1 Macro 28.00 B
2 Func 28.00 B
1 Macro 42.00 B
2 Func 42.00 B
1 Macro 63.00 B
2 Func 63.00 B
1 Macro 94.00 B
2 Func 94.00 B
1 Macro 141.00 B
2 Func 141.00 B
1 Macro 211.00 B
2 Func 211.00 B
1 Macro 316.00 B
2 Func 316.00 B
1 Macro 474.00 B
2 Func 474.00 B
1 Macro 711.00 B
2 Func 711.00 B
1 Macro 1.04 KiB
2 Func 1.04 KiB
1 Macro 1.56 KiB
2 Func 1.56 KiB
1 Macro 2.34 KiB
2 Func 2.34 KiB
1 Macro 3.51 KiB
2 Func 3.51 KiB
1 Macro 5.27 KiB
2 Func 5.27 KiB
1 Macro 7.90 KiB
2 Func 7.90 KiB
1 Macro 11.85 KiB
2 Func 11.85 KiB
1 Macro 17.78 KiB
2 Func 17.78 KiB
1 Macro 26.67 KiB
2 Func 26.67 KiB
1 Macro 40.00 KiB
2 Func 40.00 KiB
1 Macro 60.01 KiB
2 Func 60.01 KiB
1 Macro 90.01 KiB
2 Func 90.01 KiB
1 Macro 135.01 KiB
2 Func 135.01 KiB
1 Macro 202.52 KiB
2 Func 202.52 KiB
1 Macro 303.78 KiB
2 Func 303.78 KiB
1 Macro 455.67 KiB
2 Func 455.67 KiB
1 Macro 683.51 KiB
2 Func 683.51 KiB
1 Macro 1.00 MiB
2 Func 1.00 MiB
1 Macro 1.50 MiB
2 Func 1.50 MiB
1 Macro 2.25 MiB
2 Func 2.25 MiB
1 Macro 3.38 MiB
2 Func 3.38 MiB
1 Macro 5.07 MiB
2 Func 5.07 MiB
1 Macro 7.60 MiB
2 Func 7.60 MiB
1 Macro 11.40 MiB
2 Func 11.40 MiB
1 Macro 17.11 MiB
2 Func 17.11 MiB
1 Macro 25.66 MiB
2 Func 25.66 MiB
1 Macro 38.49 MiB
2 Func 38.49 MiB
1 Macro 57.74 MiB
2 Func 57.74 MiB
1 Macro 86.60 MiB
2 Func 86.60 MiB
1 Macro 129.91 MiB
2 Func 129.91 MiB
1 Macro 194.86 MiB
2 Func 194.86 MiB
1 Macro 292.29 MiB
2 Func 292.29 MiB
1 Macro 438.43 MiB
2 Func 438.43 MiB
1 Macro 657.65 MiB
2 Func 657.65 MiB
1 Macro 986.48 MiB
2 Func 986.47 MiB
1 Macro 1.45 GiB
2 Func 1.44 GiB
1 Macro 2.17 GiB
2 Func 2.17 GiB
1 Macro 3.25 GiB
2 Func 3.25 GiB
1 Macro 4.88 GiB
2 Func 4.88 GiB
1 Macro 7.32 GiB
2 Func 7.32 GiB
1 Macro 10.97 GiB
2 Func 10.97 GiB
1 Macro 16.46 GiB
2 Func 16.46 GiB
1 Macro 24.69 GiB
2 Func 24.69 GiB
1 Macro 37.03 GiB
2 Func 37.03 GiB
1 Macro 55.55 GiB
2 Func 55.55 GiB
1 Macro 83.33 GiB
2 Func 83.33 GiB
1 Macro 124.99 GiB
2 Func 124.99 GiB
1 Macro 187.49 GiB
2 Func 187.49 GiB
1 Macro 281.23 GiB
2 Func 281.23 GiB
1 Macro 421.85 GiB
2 Func 421.85 GiB
1 Macro 632.77 GiB
2 Func 632.77 GiB
1 Macro 949.16 GiB
2 Func 949.16 GiB
1 Macro 1.39 TiB
2 Func 1.39 TiB
1 Macro 2.09 TiB
2 Func 2.08 TiB
1 Macro 3.13 TiB
2 Func 3.13 TiB
1 Macro 4.69 TiB
2 Func 4.69 TiB
1 Macro 7.04 TiB
2 Func 7.04 TiB
1 Macro 10.56 TiB
2 Func 10.56 TiB
1 Macro 15.84 TiB
2 Func 15.84 TiB
1 Macro 23.76 TiB
2 Func 23.75 TiB
1 Macro 35.63 TiB
2 Func 35.63 TiB
1 Macro 53.45 TiB
2 Func 53.45 TiB
1 Macro 80.18 TiB
2 Func 80.17 TiB
1 Macro 120.26 TiB
2 Func 120.26 TiB
1 Macro 180.39 TiB
2 Func 180.39 TiB
1 Macro 270.59 TiB
2 Func 270.59 TiB
1 Macro 405.89 TiB
2 Func 405.89 TiB
1 Macro 608.83 TiB
2 Func 608.83 TiB
1 Macro 913.25 TiB
2 Func 913.25 TiB
1 Macro 1369.87 TiB
2 Func 1369.87 TiB
1 Macro 2054.81 TiB
2 Func 2054.81 TiB
1 Macro 3082.22 TiB
2 Func 3082.21 TiB
1 Macro 4623.32 TiB
2 Func 4623.32 TiB
1 Macro 6934.98 TiB
2 Func 6934.98 TiB
1 Macro 10402.48 TiB
2 Func 10402.48 TiB
1 Macro 15603.72 TiB
2 Func 15603.71 TiB
1 Macro 23405.57 TiB
2 Func 23405.57 TiB
1 Macro 35108.36 TiB
2 Func 35108.36 TiB
1 Macro 52662.54 TiB
2 Func 52662.54 TiB
1 Macro 78993.81 TiB
2 Func 78993.81 TiB
1 Macro 118490.71 TiB
2 Func 118490.71 TiB
1 Macro 177736.07 TiB
2 Func 177736.07 TiB
1 Macro 266604.11 TiB
2 Func 266604.10 TiB
1 Macro 399906.16 TiB
2 Func 399906.16 TiB
1 Macro 599859.24 TiB
2 Func 599859.24 TiB
---Next test---
1 Macro 1.00 B
2 Func 1.00 B
1 Macro 2.00 B
2 Func 2.00 B
1 Macro 4.00 B
2 Func 4.00 B
1 Macro 8.00 B
2 Func 8.00 B
1 Macro 16.00 B
2 Func 16.00 B
1 Macro 32.00 B
2 Func 32.00 B
1 Macro 64.00 B
2 Func 64.00 B
1 Macro 128.00 B
2 Func 128.00 B
1 Macro 256.00 B
2 Func 256.00 B
1 Macro 512.00 B
2 Func 512.00 B
1 Macro 1.00 KiB
2 Func 1024.00 B
1 Macro 2.00 KiB
2 Func 2.00 KiB
1 Macro 4.00 KiB
2 Func 4.00 KiB
1 Macro 8.00 KiB
2 Func 8.00 KiB
1 Macro 16.00 KiB
2 Func 16.00 KiB
1 Macro 32.00 KiB
2 Func 32.00 KiB
1 Macro 64.00 KiB
2 Func 64.00 KiB
1 Macro 128.00 KiB
2 Func 128.00 KiB
1 Macro 256.00 KiB
2 Func 256.00 KiB
1 Macro 512.00 KiB
2 Func 512.00 KiB
1 Macro 1.00 MiB
2 Func 1.00 MiB
1 Macro 2.00 MiB
2 Func 2.00 MiB
1 Macro 4.00 MiB
2 Func 4.00 MiB
1 Macro 8.00 MiB
2 Func 8.00 MiB
1 Macro 16.00 MiB
2 Func 16.00 MiB
1 Macro 32.00 MiB
2 Func 32.00 MiB
1 Macro 64.00 MiB
2 Func 64.00 MiB
1 Macro 128.00 MiB
2 Func 128.00 MiB
1 Macro 256.00 MiB
2 Func 256.00 MiB
1 Macro 512.00 MiB
2 Func 512.00 MiB
1 Macro 1.00 GiB
2 Func 1.00 GiB
1 Macro 2.00 GiB
2 Func 2.00 GiB
1 Macro 4.00 GiB
2 Func 4.00 GiB
1 Macro 8.00 GiB
2 Func 8.00 GiB
1 Macro 16.00 GiB
2 Func 16.00 GiB
1 Macro 32.00 GiB
2 Func 32.00 GiB
1 Macro 64.00 GiB
2 Func 64.00 GiB
1 Macro 128.00 GiB
2 Func 128.00 GiB
1 Macro 256.00 GiB
2 Func 256.00 GiB
1 Macro 512.00 GiB
2 Func 512.00 GiB
1 Macro 1.00 TiB
2 Func 1.00 TiB
1 Macro 2.00 TiB
2 Func 2.00 TiB
1 Macro 4.00 TiB
2 Func 4.00 TiB
1 Macro 8.00 TiB
2 Func 8.00 TiB
1 Macro 16.00 TiB
2 Func 16.00 TiB
1 Macro 32.00 TiB
2 Func 32.00 TiB
1 Macro 64.00 TiB
2 Func 64.00 TiB
1 Macro 128.00 TiB
2 Func 128.00 TiB
1 Macro 256.00 TiB
2 Func 256.00 TiB
1 Macro 512.00 TiB
2 Func 512.00 TiB
1 Macro 1024.00 TiB
2 Func 1024.00 TiB
1 Macro 2048.00 TiB
2 Func 2048.00 TiB
1 Macro 4096.00 TiB
2 Func 4096.00 TiB
1 Macro 8192.00 TiB
2 Func 8192.00 TiB
1 Macro 16384.00 TiB
2 Func 16384.00 TiB
1 Macro 32768.00 TiB
2 Func 32768.00 TiB
1 Macro 65536.00 TiB
2 Func 65536.00 TiB
1 Macro 131072.00 TiB
2 Func 131072.00 TiB
1 Macro 262144.00 TiB
2 Func 262144.00 TiB
1 Macro 524288.00 TiB
2 Func 524288.00 TiB
1 Macro 1048576.00 TiB
2 Func 1048576.00 TiB
1 Macro 2097152.00 TiB
2 Func 2097152.00 TiB
1 Macro 4194304.00 TiB
2 Func 4194304.00 TiB
1 Macro 8388608.00 TiB
2 Func 8388608.00 TiB Shorter version:
#define PriByte "%.02lf %s"
#define PriByteArg(b) (\
(unsigned long long)(b) >= (1ull << 40) ? (double)(b) / (double)(1ull << 40) : \
(unsigned long long)(b) >= (1ull << 30) ? (double)(b) / (double)(1ull << 30) : \
(unsigned long long)(b) >= (1ull << 20) ? (double)(b) / (double)(1ull << 20) : \
(unsigned long long)(b) >= (1ull << 10) ? (double)(b) / (double)(1ull << 10) : \
(double)(b)),( \
(unsigned long long)(b) >= (1ull << 40) ? "TiB" : \
(unsigned long long)(b) >= (1ull << 30) ? "GiB" : \
(unsigned long long)(b) >= (1ull << 20) ? "MiB" : \
(unsigned long long)(b) >= (1ull << 10) ? "KiB" : \
"B")WARNING: Beware of multiple evaluation, don't pass an expensive expression.
EDIT: OK so! after spending too many hours thinking about this I ended up using this:
const char *PriByteHuman(uint64_t b) {
enum { BUF_EXP_SIZE = 4, BUF_SIZE = 32 };
static int idx = 0;
static char storage_buf[1 << BUF_EXP_SIZE][BUF_SIZE];
char *buf = storage_buf[idx];
idx = (idx + 1) & ((1 << BUF_EXP_SIZE) -1);
snprintf(buf, BUF_SIZE, "%.02lf(%s)",
b >= (1ull << 40) ? (double)(b) / (double)(1ull << 40) :
b >= (1ull << 30) ? (double)(b) / (double)(1ull << 30) :
b >= (1ull << 20) ? (double)(b) / (double)(1ull << 20) :
b >= (1ull << 10) ? (double)(b) / (double)(1ull << 10) :
(double)(b),
b >= (1ull << 40) ? "TiB" : b >= (1ull << 30) ? "GiB" :
b >= (1ull << 20) ? "MiB" : b >= (1ull << 10) ? "KiB" : "B");
return buf;
// 28 bytes -> "9223372036854775807.00(TiB)\0"
}
#define PRIbyte "%s"
#define PRIbytearg(x) PriByteHuman((uint64_t)(x))I just couldn't have peace of mind knowing the previous method has the POTENTIAL of misuse thanks to multiple evaluation so I had to do it like this. The good part is that you can have up to 16 unique instances on a singular call. And it's impossible to misuse!!
Like this but imagine 16 instances instead of 5:
for (int i = 1; i < 10; ++i) {
printf(PRIbyte" "PRIbyte" "PRIbyte" "PRIbyte" "PRIbyte" \n",
PRIbytearg(i<<1), PRIbytearg(i<<2), PRIbytearg(i<<3), PRIbytearg(i<<4), PRIbytearg(i<<5)
);
}
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I'm sorry, I should have explained this more. I prefer not to return pointers to static data. Here's an example of usage: