diff options
-rw-r--r-- | spec/util_human_units_spec.lua | 15 | ||||
-rw-r--r-- | util/human/units.lua | 58 |
2 files changed, 73 insertions, 0 deletions
diff --git a/spec/util_human_units_spec.lua b/spec/util_human_units_spec.lua new file mode 100644 index 00000000..4326cdd4 --- /dev/null +++ b/spec/util_human_units_spec.lua @@ -0,0 +1,15 @@ +local units = require "util.human.units"; + +describe("util.human.units", function () + describe("format", function () + it("formats numbers with SI units", function () + assert.equal("1 km", units.format(1000, "m")); + assert.equal("1 GJ", units.format(1000000000, "J")); + assert.equal("1 ms", units.format(1/1000, "s")); + assert.equal("10 ms", units.format(10/1000, "s")); + assert.equal("1 ns", units.format(1/1000000000, "s")); + assert.equal("1 KiB", units.format(1024, "B", 'b')); + assert.equal("1 MiB", units.format(1024*1024, "B", 'b')); + end); + end); +end); diff --git a/util/human/units.lua b/util/human/units.lua new file mode 100644 index 00000000..2c4662cd --- /dev/null +++ b/util/human/units.lua @@ -0,0 +1,58 @@ +local large = { + "k", 1000, + "M", 1000000, + "G", 1000000000, + "T", 1000000000000, + "P", 1000000000000000, + "E", 1000000000000000000, + "Z", 1000000000000000000000, + "Y", 1000000000000000000000000, +} +local small = { + "m", 0.001, + "μ", 0.000001, + "n", 0.000000001, + "p", 0.000000000001, + "f", 0.000000000000001, + "a", 0.000000000000000001, + "z", 0.000000000000000000001, + "y", 0.000000000000000000000001, +} + +local binary = { + "Ki", 2^10, + "Mi", 2^20, + "Gi", 2^30, + "Ti", 2^40, + "Pi", 2^50, + "Ei", 2^60, + "Zi", 2^70, + "Yi", 2^80, +} + +-- n: number, the number to format +-- unit: string, the base unit +-- b: optional enum 'b', thousands base +local function format(n, unit, b) --> string + local round = math.floor; + local prefixes = large; + local logbase = 1000; + local fmt = "%.3g %s%s"; + if n == 0 then + return fmt:format(n, "", unit); + end + if b == 'b' then + prefixes = binary; + logbase = 1024; + elseif n < 1 then + prefixes = small; + round = math.ceil; + end + local m = math.max(0, math.min(8, round(math.abs(math.log(math.abs(n), logbase))))); + local prefix, multiplier = table.unpack(prefixes, m * 2-1, m*2); + return fmt:format(n / (multiplier or 1), prefix or "", unit); +end + +return { + format = format; +}; |