Skip to content

Instantly share code, notes, and snippets.

@dgoguerra
Last active July 23, 2026 23:58
Show Gist options
  • Select an option

  • Save dgoguerra/7194777 to your computer and use it in GitHub Desktop.

Select an option

Save dgoguerra/7194777 to your computer and use it in GitHub Desktop.
Format a quantity in bytes into a human readable string (C)
#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;
}
@Woynert

Woynert commented Jul 23, 2026

Copy link
Copy Markdown

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