diff options
author | Explorer09 <explorer09@gmail.com> | 2023-10-27 18:34:47 +0800 |
---|---|---|
committer | BenBE <BenBE@geshi.org> | 2023-11-24 09:51:25 +0100 |
commit | 5751bafb8db06b843a9a79e100751cfcadc09f90 (patch) | |
tree | 8771ae3f5c4068b7cf1db3b84de3240b3da1dfbe | |
parent | e34a9fcc8cda01e4a58f59b4a9d990eda8effd60 (diff) |
Rewrite Meter_humanUnit() to accept floating point value
Since Meter_humanUnit() is often called with floating point values in
Meter objects, rewrite the function to let it process `double` type
natively, and save floating point to integer casts.
The rewritten function:
* Allows higher orders of magnitude including 'R' and 'Q', and
addresses infinity. (The previous version has a maximum value of
(2^64 - 1) representing 16 ZiB.)
* Rounds values when they are in intervals (99.9, 100) and (9.99, 10),
and displays them with correct precision (number of fraction digits).
* Produces assertion error on negative and NaN values (undefined
behavior).
Signed-off-by: Kang-Che Sung <explorer09@gmail.com>
-rw-r--r-- | Meter.c | 46 | ||||
-rw-r--r-- | Meter.h | 4 | ||||
-rw-r--r-- | XUtils.h | 3 | ||||
-rw-r--r-- | pcp/PCPDynamicMeter.c | 20 |
4 files changed, 43 insertions, 30 deletions
@@ -51,32 +51,40 @@ Meter* Meter_new(const Machine* host, unsigned int param, const MeterClass* type return this; } -int Meter_humanUnit(char* buffer, unsigned long int value, size_t size) { - const char* prefix = "KMGTPEZY"; - unsigned long int powi = 1; - unsigned int powj = 1, precision = 2; - - for (;;) { - if (value / 1024 < powi) - break; - - if (prefix[1] == '\0') +/* Converts 'value' in kibibytes into a human readable string. + Example output strings: "0K", "1023K", "98.7M" and "1.23G" */ +int Meter_humanUnit(char* buffer, double value, size_t size) { + size_t i = 0; + + assert(value >= 0.0); + while (value >= ONE_K) { + if (i >= ARRAYSIZE(unitPrefixes) - 1) { + if (value > 9999.0) { + return snprintf(buffer, size, "inf"); + } break; + } - powi *= 1024; - ++prefix; + value /= ONE_K; + ++i; } - if (*prefix == 'K') - precision = 0; + int precision = 0; - for (; precision > 0; precision--) { - powj *= 10; - if (value / powi < powj) - break; + if (i > 0) { + // Fraction digits for mebibytes and above + precision = value <= 99.9 ? (value <= 9.99 ? 2 : 1) : 0; + + // Round up if 'value' is in range (99.9, 100) or (9.99, 10) + if (precision < 2) { + double limit = precision == 1 ? 10.0 : 100.0; + if (value < limit) { + value = limit; + } + } } - return snprintf(buffer, size, "%.*f%c", precision, (double) value / powi, *prefix); + return snprintf(buffer, size, "%.*f%c", precision, value, unitPrefixes[i]); } void Meter_delete(Object* cast) { @@ -146,7 +146,9 @@ extern const MeterClass Meter_class; Meter* Meter_new(const Machine* host, unsigned int param, const MeterClass* type); -int Meter_humanUnit(char* buffer, unsigned long int value, size_t size); +/* Converts 'value' in kibibytes into a human readable string. + Example output strings: "0K", "1023K", "98.7M" and "1.23G" */ +int Meter_humanUnit(char* buffer, double value, size_t size); void Meter_delete(Object* cast); @@ -111,4 +111,7 @@ int compareRealNumbers(double a, double b); nonnegative. */ double sumPositiveValues(const double* array, size_t count); +/* IEC unit prefixes */ +static const char unitPrefixes[] = { 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y', 'R', 'Q' }; + #endif diff --git a/pcp/PCPDynamicMeter.c b/pcp/PCPDynamicMeter.c index 241153a0..1e118351 100644 --- a/pcp/PCPDynamicMeter.c +++ b/pcp/PCPDynamicMeter.c @@ -351,27 +351,27 @@ void PCPDynamicMeter_updateValues(PCPDynamicMeter* this, Meter* meter) { break; case PM_TYPE_32: bytes += conv.dimSpace ? - Meter_humanUnit(buffer + bytes, atom.l, size - bytes) : + Meter_humanUnit(buffer + bytes, (double) atom.l, size - bytes) : xSnprintf(buffer + bytes, size - bytes, "%d", atom.l); break; case PM_TYPE_U32: bytes += conv.dimSpace ? - Meter_humanUnit(buffer + bytes, atom.ul, size - bytes) : + Meter_humanUnit(buffer + bytes, (double) atom.ul, size - bytes) : xSnprintf(buffer + bytes, size - bytes, "%u", atom.ul); break; case PM_TYPE_64: bytes += conv.dimSpace ? - Meter_humanUnit(buffer + bytes, atom.ll, size - bytes) : + Meter_humanUnit(buffer + bytes, (double) atom.ll, size - bytes) : xSnprintf(buffer + bytes, size - bytes, "%lld", (long long) atom.ll); break; case PM_TYPE_U64: bytes += conv.dimSpace ? - Meter_humanUnit(buffer + bytes, atom.ull, size - bytes) : + Meter_humanUnit(buffer + bytes, (double) atom.ull, size - bytes) : xSnprintf(buffer + bytes, size - bytes, "%llu", (unsigned long long) atom.ull); break; case PM_TYPE_FLOAT: bytes += conv.dimSpace ? - Meter_humanUnit(buffer + bytes, atom.f, size - bytes) : + Meter_humanUnit(buffer + bytes, (double) atom.f, size - bytes) : xSnprintf(buffer + bytes, size - bytes, "%.2f", (double) atom.f); break; case PM_TYPE_DOUBLE: @@ -427,27 +427,27 @@ void PCPDynamicMeter_display(PCPDynamicMeter* this, ATTR_UNUSED const Meter* met break; case PM_TYPE_32: len = conv.dimSpace ? - Meter_humanUnit(buffer, atom.l, sizeof(buffer)) : + Meter_humanUnit(buffer, (double) atom.l, sizeof(buffer)) : xSnprintf(buffer, sizeof(buffer), "%d", atom.l); break; case PM_TYPE_U32: len = conv.dimSpace ? - Meter_humanUnit(buffer, atom.ul, sizeof(buffer)) : + Meter_humanUnit(buffer, (double) atom.ul, sizeof(buffer)) : xSnprintf(buffer, sizeof(buffer), "%u", atom.ul); break; case PM_TYPE_64: len = conv.dimSpace ? - Meter_humanUnit(buffer, atom.ll, sizeof(buffer)) : + Meter_humanUnit(buffer, (double) atom.ll, sizeof(buffer)) : xSnprintf(buffer, sizeof(buffer), "%lld", (long long) atom.ll); break; case PM_TYPE_U64: len = conv.dimSpace ? - Meter_humanUnit(buffer, atom.ull, sizeof(buffer)) : + Meter_humanUnit(buffer, (double) atom.ull, sizeof(buffer)) : xSnprintf(buffer, sizeof(buffer), "%llu", (unsigned long long) atom.ull); break; case PM_TYPE_FLOAT: len = conv.dimSpace ? - Meter_humanUnit(buffer, atom.f, sizeof(buffer)) : + Meter_humanUnit(buffer, (double) atom.f, sizeof(buffer)) : xSnprintf(buffer, sizeof(buffer), "%.2f", (double) atom.f); break; case PM_TYPE_DOUBLE: |