Skip to content

Instantly share code, notes, and snippets.

@pkkm
Last active December 18, 2021 16:41
Show Gist options
  • Save pkkm/629a66d47ecd16aa89e8b67ba5abd77d to your computer and use it in GitHub Desktop.
Save pkkm/629a66d47ecd16aa89e8b67ba5abd77d to your computer and use it in GitHub Desktop.
Criterion-like formatting of numbers
#define _POSIX_C_SOURCE 200809L // For strdup.
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include <math.h> // Remember to link with -lm.
#define ARRAY_LEN(array) (sizeof(array) / sizeof((array)[0]))
typedef struct {
int exponent;
const char *symbol;
const char *bin_symbol;
} Prefix;
Prefix PREFIXES[] = {
{8, "Y", "Yi"},
{7, "Z", "Zi"},
{6, "E", "Ei"},
{5, "P", "Pi"},
{4, "T", "Ti"},
{3, "G", "Gi"},
{2, "M", "Mi"},
{1, "k", "Ki"},
{0, "", ""},
{-1, "m", NULL},
{-2, "μ", NULL},
{-3, "n", NULL},
{-4, "p", NULL},
{-5, "f", NULL},
{-6, "a", NULL},
{-7, "z", NULL},
{-8, "y", NULL}
};
char *format_num_with_prefix(double num, const char *prefix) {
// Format to 4 digits, even if they are trailing zeros.
double num_abs = fabs(num);
char buffer[128];
if (num_abs >= 1e9) {
snprintf(buffer, ARRAY_LEN(buffer), "%.4g %s", num, prefix);
return strdup(buffer);
}
for (int exponent = 3; exponent >= 1; exponent--) {
if (num_abs >= pow(10, exponent)) {
snprintf(buffer, ARRAY_LEN(buffer), "%.*f %s", 3 - exponent, num, prefix);
return strdup(buffer);
}
}
snprintf(buffer, ARRAY_LEN(buffer), "%.3f %s", num, prefix);
return strdup(buffer);
}
char *format_num(double num, bool binary) {
// Format a number with a SI prefix.
// Based on the Criterion library for Haskell.
// Alternative: <http://jkorpela.fi/c/eng.html>.
assert(isfinite(num));
double num_abs = fabs(num);
double base = binary ? 1024 : 1000;
for (size_t i_prefix = 0; i_prefix < ARRAY_LEN(PREFIXES); i_prefix++) {
Prefix prefix = PREFIXES[i_prefix];
const char *symbol = binary ? prefix.bin_symbol : prefix.symbol;
if (symbol == NULL) {
continue;
}
if (num_abs >= pow(base, prefix.exponent)) {
return format_num_with_prefix(num * pow(base, -prefix.exponent), symbol);
}
}
char buffer[128];
snprintf(buffer, ARRAY_LEN(buffer), "%g ", num);
return strdup(buffer);
}
int main(int argc, char **argv) {
double example_time = 0.000005235724;
char *example_time_str = format_num(example_time, false);
printf("Example time: %ss\n", example_time_str);
free(example_time_str);
int example_size = 52 * 1024 * 1024;
char *example_size_str = format_num(example_size, true);
printf("Example file size: %sB\n", example_size_str);
free(example_size_str);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment