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

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 

@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