This is nice and compact, but only works for numbers up to around 9.2 * 1e18. After that it just gives “M” as the suffix. Here’s a version that works up to ~10e59 or higher
--[[
Each value is the prefix corresponding to a power of 10 given by the key.
]]
local METRIC_PREFIXES = {
[3 * 0] = "",
[3 * 1] = "k",
[3 * 2] = "m",
[3 * 3] = "b",
[3 * 4] = "t",
[3 * 5] = "q",
[3 * 6] = "qn",
[3 * 7] = "sx",
[3 * 8] = "sp",
[3 * 9] = "oc",
[3 * 10] = "no",
[3 * 11] = "de",
[3 * 12] = "ude",
[3 * 13] = "dde",
[3 * 14] = "tde",
[3 * 15] = "qde",
[3 * 16] = "qnde",
[3 * 17] = "sxde",
[3 * 18] = "spde",
[3 * 19] = "ocde",
[3 * 20] = "node",
[3 * 21] = "v",
}
--[[
Given a table t and a numeric index i, find the greatest populated index of t less than or equal to i.
t should be a dictionary from numbers to non-nil values.
]]
function greatest_index_lte(t, i)
assert(i >= 0, ("Cannot reliably search for indices less than 0 (got %g)!"):format(i))
for j = math.floor(i), 0, -1 do
if t[j] ~= nil then
return j
end
end
error(("No indices under %g found!"):format(math.floor(index)))
end
function format_metric_suffix(n, number_format)
local n_log10 = math.log10(n)
local prefix_index = greatest_index_lte(METRIC_PREFIXES, math.floor(n_log10))
local prefix = METRIC_PREFIXES[prefix_index]
local n_remainder = n / math.pow(10, prefix_index)
number_format = (number_format or "%g") .. "%s"
return number_format:format(n_remainder, prefix)
end
print(format_metric_suffix(10e59))
It can also easily be partly memoized for a 2x speed improvement:
--[[
Each value is the prefix corresponding to a given power of 10.
]]
local METRIC_PREFIXES =
[3 * 0] = "",
[3 * 1] = "k",
[3 * 2] = "m",
[3 * 3] = "b",
[3 * 4] = "t",
[3 * 5] = "q",
[3 * 6] = "qn",
[3 * 7] = "sx",
[3 * 8] = "sp",
[3 * 9] = "oc",
[3 * 10] = "no",
[3 * 11] = "de",
[3 * 12] = "ude",
[3 * 13] = "dde",
[3 * 14] = "tde",
[3 * 15] = "qde",
[3 * 16] = "qnde",
[3 * 17] = "sxde",
[3 * 18] = "spde",
[3 * 19] = "ocde",
[3 * 20] = "node",
[3 * 21] = "v",
}
--[[
Curries the first parameter of a function.
]]
function curried(f, value)
return function(...)
return f(value, ...)
end
end
--[[
Memoizes a (pure) function of 1 parameter.
]]
function memoized(f)
local known_inputs = {}
return function(input)
local known_value = known_inputs[input]
if known_value then
--print("Known value")
return known_value
else
local value = f(input)
--print("New value", value)
known_inputs[input] = value
return value
end
end
end
--[[
Given a table t and a numeric index i, find the greatest populated index of t less than or equal to i.
t should be a dictionary from numbers to non-nil values.
]]
function greatest_index_lte(t, i)
assert(i >= 0, ("Cannot reliably search for indices less than 0 (got %g)!"):format(i))
for j = math.floor(i), 0, -1 do
if t[j] ~= nil then
return j
end
end
error(("No indices under %g found!"):format(math.floor(index)))
end
local memoized_greatest_index_lte = memoized(curried(greatest_index_lte, METRIC_PREFIXES))
function format_metric_suffix(n, number_format)
local n_log10 = math.log10(n)
local prefix_index = memoized_greatest_index_lte(math.floor(n_log10))
local prefix = METRIC_PREFIXES[prefix_index]
local n_remainder = n / math.pow(10, prefix_index)
number_format = (number_format or "%g") .. "%s"
return number_format:format(n_remainder, prefix)
end