Is it possible to get the numbers over 1k, 10k, 100k, 1M, and so on to have decimals and be coded more efficient instead of 'if value > 1000 then text = "1k"'. Also having decimals as in 1.1k and 101k?
This is a similar problem to writing the decimal in exponential notation. How would we do that?
We can get the number of digits on (positive) numbers using math.log10
:
math.log10(1) -- 0 math.log10(2) -- 0.30102999566398 math.log10(9.99) -- 0.99956548822598 math.log(10) -- 1 math.log(11) -- 1.04139 math.log(150) -- 2.17609
The number of digits is the whole-number-part of the base-10 log. We can get the whole-number-part with math.floor
Thus the exponential part would be exp = 10^math.floor( math.log10(n) )
. The fractional part would be n / exp
:
function exponential(n) local power = math.floor( math.log10(n) ) local exp = 10 ^ power return (n / exp) .. "e" .. power end print(exponential(101000)) -- 1.01e5
But what if we want to limit the number of digits that will be after the exponent? E.g., math.pi
will produce 3.1415926535898e0
. We just need to format the n / exp
part using string.format
:
function exponential(n) local power = math.floor( math.log10(n) ) local exp = 10 ^ power return string.format("%.1f", n / exp) .. "e" .. power end print(exponential(math.pi)) -- 3.1e0
EDIT: A problem is exponential(1)
. It'll respond 1.0e0
rather than 1e0
. I don't think Lua has this exactly built-in, so we have to do it ourself. Luckily, it's not too bad:
function shortenDecimal(n) if n == math.floor(n) then -- no fractional part return tostring(n) else return string.format("%.1f", n) end end function exponential(n) local power = math.floor( math.log10(n) ) local exp = 10 ^ power return shortenDecimal(n / exp) .. "e" .. power end
This is close to what we want.
If the power
is 3
, we would just want to show "k" instead of "e3". If it was 6, we would want "M", etc.
But if the power
is 4
, we want to adjust to be 3
-- 10.1k
instead of 1.01e4
(note the extra digit before the decimal). We can get this pretty easily using %
(mod) -- remainder after division.
Mod is frequently used to "trim off" "extra" amounts. Since we only want clean multiples of three...
power - power % 3
will cut off the extra:
function formatted(n) local power = math.floor( math.log10(n) ) power = power - power % 3 local exp = 10 ^ power return shortenDecimal(n / exp) .. "e" .. power end print(formatted(123456)) -- 123.5e3
Finally, we need to show something instead of e3
. We could either use power / 3 + 1
to get a list:
number | power | list index |
---|---|---|
1 | 0 | 1 |
1000 | 3 | 2 |
1000000 | 6 | 3 |
function formatted(n) local names = {"", "k", "M", "B", "T"} local power = math.floor( math.log10(n) ) power = power - power % 3 local exp = 10 ^ power return shortenDecimal(n / exp) .. names[power / 3 + 1] end print(formatted(123456)) -- 123.5k
We could also have used a dictionary:
function formatted(n) local name = {[0] = "", [3] = "k", [6] = "M", [9] = "B", [12] = "T"} local power = math.floor( math.log10(n) ) power = power - power % 3 local exp = 10 ^ power return shortenDecimal(n / exp) .. named[power] end
You could easily do that with dividing.
Just do:
if number>1000 then text=tostring(number/1000).."k" elseif number>10^6 then text=tostring(number/10^6).."M" end
Following KoreanBBQ's answer, I made a rounding function. It doesn't work as intended when deaing with negative numbers, but otherwise it's pretty stable.
number = 123456789 text = "" function round(number, places) if number == math.floor(number) or places < 1 or places ~= math.floor(places) then return number end local whole, fractional = math.modf(number) fractional = tostring(fractional) local changing = string.sub(fractional, places + 2, places + 2) changing = tonumber(changing) local nextnumber = string.sub(fractional, places + 3, places + 3) nextnumber = tonumber(nextnumber) if nextnumber >= 5 then changing = changing + 1 end changing = tostring(changing) fractional = string.sub(fractional, 1, places + 2) fractional = string.gsub(fractional, string.sub(fractional, places + 2, places + 2), changing) fractional = tonumber(fractional) return whole + fractional end if number > 1000 and number < 10^6 then text = round(number/1000, 1).."k" elseif number > 10^6 then text = round(number/10^6, 1).."M" end