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; | |
| } |
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
Here is a way to do it without functions or scary static memory and in constant time.
Usage:
printf("My size is "PriByte, PriByteArg(1024));Here are some tests comparing it with the
humanSizefunction from OP, check it out:CLICK TO EXPAND.