From d9c9d465dffb2ed67e43cf9425f30dae6012e93a Mon Sep 17 00:00:00 2001 From: Andrew Bettison Date: Tue, 20 Sep 2016 15:46:17 +0930 Subject: [PATCH] Add approximate scaled format functions Used to produce human-readable values in logs, reports etc. Eg: 1,073,741,824 -> "1.07g" (SI) and "1.00G" (binary). --- numeric_str.c | 29 +++++++++++++++++++++++++++++ numeric_str.h | 20 ++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/numeric_str.c b/numeric_str.c index c826bb02..2ac411ed 100644 --- a/numeric_str.c +++ b/numeric_str.c @@ -304,3 +304,32 @@ int str_to_uint64_interval_ms(const char *str, int64_t *result, const char **aft return 1; } +strbuf strbuf_append_double_scaled_binary(strbuf sb, double value) +{ + const char *suffix = " KMGTP"; + while (value >= 1024 && suffix[1]){ + value = value / 1024; + suffix++; + } + int precision = value < 10 ? 2 : value < 100 ? 1 : 0; + strbuf_sprintf(sb, "%.*f", precision, value); + if (*suffix && *suffix != ' ') { + strbuf_putc(sb, *suffix); + } + return sb; +} + +strbuf strbuf_append_double_scaled_si(strbuf sb, double value) +{ + const char *suffix = " kmgtp"; + while (value >= 1000 && suffix[1]){ + value = value / 1000; + suffix++; + } + int precision = value < 10 ? 2 : value < 100 ? 1 : 0; + strbuf_sprintf(sb, "%.*f", precision, value); + if (*suffix && *suffix != ' ') { + strbuf_putc(sb, *suffix); + } + return sb; +} diff --git a/numeric_str.h b/numeric_str.h index 7f7cb453..4f426498 100644 --- a/numeric_str.h +++ b/numeric_str.h @@ -107,6 +107,26 @@ uint64_t scale_factor(const char *str, const char **afterp); strbuf strbuf_append_uint32_scaled(strbuf sb, uint32_t value); strbuf strbuf_append_uint64_scaled(strbuf sb, uint64_t value); +/* Append a double value to a strbuf in ASCII decimal fixed-point format with three significant + * digits, optionally scaled with either a binary scale suffix in the set {KMGTP}: 'K' = 1<<10, 'M' + * = 1<<20, 'G' = * 1<<30, etc., or an S.I. scale suffix in the set {kmgtp}: 'k' = 1e3, 'm' = 1e6, + * etc. This format is lossy because it always applies the scale, which may truncate insignificant + * digits. + * + * Eg, binary S.I. + * 1000 -> "1000" 1000 -> "1.00k" + * 1001 -> "1001" 1001 -> "1.00k" + * 1024 -> "1.00K" 1024 -> "1.02k" + * 1025 -> "1.00K" 1025 -> "1.03k" + * + * @author Andrew Bettison + */ +strbuf strbuf_append_double_scaled_binary(strbuf sb, double value); +strbuf strbuf_append_double_scaled_si(strbuf sb, double value); + +#define alloca_double_scaled_binary(v) strbuf_str(strbuf_append_double_scaled_binary(strbuf_alloca(10), (v))) +#define alloca_double_scaled_si(v) strbuf_str(strbuf_append_double_scaled_si(strbuf_alloca(10), (v))) + /* Parse a string as a time interval (seconds) in millisecond resolution. Return the number of * milliseconds. Valid strings are all unsigned ASCII decimal numbers with up to three digits after * the decimal point.