summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorExplorer09 <explorer09@gmail.com>2023-10-27 18:34:47 +0800
committerBenBE <BenBE@geshi.org>2023-11-24 09:51:25 +0100
commit5751bafb8db06b843a9a79e100751cfcadc09f90 (patch)
tree8771ae3f5c4068b7cf1db3b84de3240b3da1dfbe
parente34a9fcc8cda01e4a58f59b4a9d990eda8effd60 (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.c46
-rw-r--r--Meter.h4
-rw-r--r--XUtils.h3
-rw-r--r--pcp/PCPDynamicMeter.c20
4 files changed, 43 insertions, 30 deletions
diff --git a/Meter.c b/Meter.c
index 12c8c941..64e88071 100644
--- a/Meter.c
+++ b/Meter.c
@@ -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) {
diff --git a/Meter.h b/Meter.h
index e0ca1f07..6b14634a 100644
--- a/Meter.h
+++ b/Meter.h
@@ -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);
diff --git a/XUtils.h b/XUtils.h
index cfb375cc..afe22112 100644
--- a/XUtils.h
+++ b/XUtils.h
@@ -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:

© 2014-2024 Faster IT GmbH | imprint | privacy policy