Иногда забавно создать повторно используемый, даже в строке printf()
, макрос / функцию для обработки широкого диапазона значений.
#include <assert.h>
#include <limits.h>
#include <stdio.h>
#include <math.h>
// https://stackoverflow.com/a/4589384/2410359
#define IMAX_BITS(m) ((m)/((m)%255+1) / 255%255*8 + 7-86/((m)%255+12))
// Decimal digits possible in an integer `m`
#define IMAX_DIGITS(m) (IMAX_BITS(m)*28/93 + 1) //ceiling( bits * log10(2))
#define ULLONG_STRING_SIZE (IMAX_DIGITS(ULLONG_MAX) + 1)
#define KSCALE 1000u /* or 1024u */
char* PrintMetric(char *dest, size_t n, unsigned long long b, char *suffix) {
char prefix[] = "\0kMGTPEZY";
int i = 0;
while (i < sizeof prefix - 1 && b / pow(KSCALE, i) >= 99.95) {
i++;
}
assert(i < sizeof prefix - 1);
snprintf(dest, n, "%-.1f %.1s%s", b / pow(KSCALE, i), prefix + i, suffix);
return dest;
}
#define PRT_BYTES(ull) \
(PrintMetric((char [ULLONG_STRING_SIZE]){0}, ULLONG_STRING_SIZE, (ull), "b"))
// ^----------------------------^ Compound literal
Использование
Обратите внимание на многократное использование в printf()
.
#define GSCALE (1ull * KSCALE * KSCALE * KSCALE)
void fooo() {
puts("Name Available Required");
puts("------------- ----------------- ---------------");
// Notice 2 calls to PRT_BYTES()
printf("%-*s %-*s %-s\n", 13, "Something", 17, PRT_BYTES(10.1*GSCALE), PRT_BYTES(2.3*GSCALE));
printf("%-*s %-*s %-s\n", 13, "Something", 17, PRT_BYTES(99949), PRT_BYTES(99950));
printf("%-*s %-*s %-s\n", 13, "Something", 17, PRT_BYTES(0), PRT_BYTES(ULLONG_MAX));
}
int main() {
fooo();
return 0;
}
Вывод
Name Available Required
------------- ----------------- ---------------
Something 10.1 Gb 2.3 Gb
Something 99.9 kb 0.1 Mb
Something 0.0 b 18.4 Eb