Abbreviation system not showing accurately

  1. What do you want to achieve? I want it show accurately

  2. What is the issue? it shows numbers like 0.0 Sp/ 0 Qi

  3. **What solutions have you tried so far?**I tried searching in devforum

local player = game.Players.LocalPlayer
local ST = player.Storage
local strength = player.leaderstats.Strength

local suffixlist = {"" , "k" , "M" , "B" , "T" , "Qa" , "Qi" , "s" , "Sp" , "O" , "N" , "D" , "Und" , "Dnd" , "Trd" , "Qad" , "Qid" , "Sid" , "Spd" , "Ocd" , "Ncd" , "V" , "G" , "Gp"}


local function Format(value , idp)
	local exp = math.floor(math.log(math.max(1, math.abs(value), 1000)))
	local suffix = suffixlist[1 + exp] or ("e+" .. exp)
	local norm = math.floor(value *((10 ^ idp)/(1000 ^ exp)))/(10^idp)

	return("%.".. idp .. "f%s"):format(norm, suffix)
end
while wait() do
	if strength.Value <= 999 then
		
		script.Parent.Text = Format(strength.Value,0).."/"..Format(ST.Value,0)
	elseif strength.Value > 999 then
		script.Parent.Text = Format(strength.Value,1).."/"..Format(ST.Value,0)
	end
end

With one search I found this.
I adjusted the script for you.

local player = game.Players.LocalPlayer
local ST = player.Storage
local strength = player.leaderstats.Strength

local suffixlist = {"" , "k" , "M" , "B" , "T" , "Qa" , "Qi" , "s" , "Sp" , "O" , "N" , "D" , "Und" , "Dnd" , "Trd" , "Qad" , "Qid" , "Sid" , "Spd" , "Ocd" , "Ncd" , "V" , "G" , "Gp"}

local decimals = 1
local function Format(x)
	local visible = nil
	local suffix = nil
	if x < 1000 then
		visible = x * math.pow(10, decimals)
		suffix = ""
	else
		local digits = math.floor(math.log10(x)) + 1
		local index = math.min(#suffixlist, math.floor((digits - 1) / 3))
		visible = x / math.pow(10, index * 3 - decimals)
		suffix = suffixlist[index] .. "+"
	end
	local front = visible / math.pow(10, decimals)
	local back = visible % math.pow(10, decimals)

	if decimals > 0 then
		return string.format("%i.%0." .. tostring(decimals) .. "i%s", front, back, suffix)
	else
		return string.format("%i%s", front, suffix)
	end
end

while wait() do
	if strength.Value <= 999 then

		script.Parent.Text = Format(strength.Value,0).."/"..Format(ST.Value)
	elseif strength.Value > 999 then
		script.Parent.Text = Format(strength.Value,1).."/"..Format(ST.Value)
	end
end

Edit: linked wrong post :-p

1 Like

here is another method

local function NumberToText(value)
	if value >= 1000000 then
		return string.format("%.3fM", value / 1000000)
	elseif value >= 1000 then
		return string.format("%.3fK", value / 1000)
	else
		return string.format("%.3f", value)
	end
end

local number = math.random(0, 3842837)
local text = NumberToText(number)
print(text, number)

Your script is broken.

local function NumberToText(value)
	if value >= 1000000 then
		return string.format("%.2fM", value / 1000000)
		elseif value >= 1000 then
	return string.format("%.2fK", value / 1000)
	else
	return string.format("%.2f", value)
	end
	end

local number = 454364362632
local text = NumberToText(number)
print(text, number)

image
Not that good either unless you add 2 extra lines for each suffix.

true but your function is already 19 lines of code
so my method can support upto 9 suffix’s before the function is longer then yours

You can add infinitely more suffixes without adding more lines of code.

you need to be careful with very large numbers you might get precision errors it would be safer to split your number into multiple variables

I would not want my value to get any higher then 100,000,000,000,000 (one hundred trillion)

i personally don’t think there is any need to do fancy maths for this but if i did this is how i would do it

local suffixList = {"k", "M", "B" , "T", "Qa", "Qi", "s", "Sp", "O", "N", "D", "Und", "Dnd", "Trd", "Qad", "Qid", "Sid", "Spd", "Ocd", "Ncd", "V", "G", "Gp"}

local function NumberToString(value)
	local index = math.max(math.floor(math.log10(math.abs(value)) / 3), 0)
	value /= math.pow(10, index * 3)
	local suffix = suffixList[index] or ""
	return string.format("%.2f%s", value, suffix)
end

local number = math.random(-1000000, 1000000)
local text = NumberToString(number)
print(text, number)

OP’s solution is clever to use the format string to get the decimals.

The difference between mine (the one @CentiDev linked) and OP’s is that OP’s will round the decimal part at the end, but mine will always just round down (truncate the number).

There were a few things wrong with OPs approach though, I commented everything I needed to change to get it working:

local suffixlist = {"" , "k" , "M" , "B" , "T" , "Qa" , "Qi" , "s" , "Sp" , "O" , "N" , "D" , "Und" , "Dnd" , "Trd" , "Qad" , "Qid" , "Sid" , "Spd" , "Ocd" , "Ncd" , "V" , "G" , "Gp"}

local function Format(value, idp)
  idp = idp or 0
  -- replace with log10
  -- remove 1000 in math.max
  -- add / 3 at the end
  local exp = math.floor(math.log10(math.max(1, math.abs(value))) / 3)
  local suffix = suffixlist[1 + exp] or ("e+" .. exp)
  -- remove the /(1000^exp) thing
  local norm = math.floor(value * 10^idp)/(10^idp)
  -- add / 1000^exp to the norm
  return("%.".. idp .. "f%s"):format(norm/1000^exp, suffix)
end
Results and tests
-- tests
function test(x, decimals, expected)
    local res = Format(x, decimals)
    print(tostring(x), "to " .. tostring(decimals) .. " places ->", res)
    assert(res == expected)
end

test(0, nil, "0")
test(0, 4, "0.0000")
test(1001, 2, "1.00k")
test(1001, 3, "1.001k")
test(100234, 2, "100.23k")
test(1002345678, 2, "1.00B")
test(1.23456789e35, nil, "123D")
test(1.23456789e35, 2, "123.46D") -- note: rounds up
test(1.23456789e44, 2, "123.46Trd") -- note: rounds up

--[[ results:
0   to nil places ->    0
0   to 4 places ->  0.0000
1001    to 2 places ->  1.00k
1001    to 3 places ->  1.001k
100234  to 2 places ->  100.23k
1002345678  to 2 places ->  1.00B
1.23456789e+35  to nil places ->    123D
1.23456789e+35  to 2 places ->  123.46D
1.23456789e+44  to 2 places ->  123.46Trd
]]

For what its worth, I think @5uphi’s original if-cascade solution is great, easy-to-read, and fast enough (potentially faster than the math-based version), if you always know your numbers are under a certain size. Mine is a general-purpose solution, but theirs is much easier to understand at a glance.

For those of you that enjoy micro-optimizing, here’s some benchmarks on how long it took to format a few million numbers of varying sizes.

First number is average across a wide logarithmic scale / second number is average across small numbers 0 - 1000.

@5uphi’s if cascade: 707 ms / 863 ms small
fixed version of @itzmerose_12’s: 768 ms / 684 ms
my original hack: 848 ms / 974ms

test code
--!strict

wait(1)

local format = string.format
local pow, floor, log10, min, max, abs = math.pow, math.floor, math.log10, math.min, math.max, math.abs
 	
local suffixlist = {"", "k" , "M" , "B" , "T" , "Qa" , "Qi" , "s" , "Sp" , "O" , "N" , "D" , "Und" , "Dnd" , "Trd" , "Qad" , "Qid" , "Sid" , "Spd" , "Ocd" , "Ncd" , "V" , "G" , "Gp"}

local function AbbreviateWithIfs(x: number, decimals: number?)
	if decimals == nil then decimals = 0 end
	
	local fmt = "%." .. decimals .. "f%s"	
	if x >= 1e69 then return format(fmt, x / 1e69, "Gp")
	elseif x >= 1e66 then return format(fmt, x / 1e66, "G")
	elseif x >= 1e63 then return format(fmt, x / 1e63, "V")
	elseif x >= 1e60 then return format(fmt, x / 1e60, "Ncd")
	elseif x >= 1e57 then return format(fmt, x / 1e57, "Ocd")
	elseif x >= 1e54 then return format(fmt, x / 1e54, "Spd")
	elseif x >= 1e51 then return format(fmt, x / 1e51, "Sid")
	elseif x >= 1e48 then return format(fmt, x / 1e48, "Qid")
	elseif x >= 1e45 then return format(fmt, x / 1e45, "Qad")
	elseif x >= 1e42 then return format(fmt, x / 1e42, "Trd")
	elseif x >= 1e39 then return format(fmt, x / 1e39, "Dnd")
	elseif x >= 1e36 then return format(fmt, x / 1e36, "Und")
	elseif x >= 1e33 then return format(fmt, x / 1e33, "D")
	elseif x >= 1e30 then return format(fmt, x / 1e30, "N")
	elseif x >= 1e27 then return format(fmt, x / 1e27, "O")
	elseif x >= 1e24 then return format(fmt, x / 1e24, "Sp")
	elseif x >= 1e21 then return format(fmt, x / 1e21, "s")
	elseif x >= 1e18 then return format(fmt, x / 1e18, "Qi")
	elseif x >= 1e15 then return format(fmt, x / 1e15, "Qa")
	elseif x >= 1e12 then return format(fmt, x / 1e12, "T")
	elseif x >= 1e9 then return format(fmt, x / 1e9, "B")
	elseif x >= 1e6 then return format(fmt, x / 1e6, "M")
	elseif x >= 1e3 then return format(fmt, x / 1e3, "k")
	else return format(fmt, x, "")
	end
end

local function AbbreviateWithFormat(x: number, decimals: number?)
	if decimals == nil then decimals = 0 end
	local places = pow(10, decimals)
	local exp = floor(log10(max(1, abs(x))) / 3)
	local suffix = suffixlist[1 + exp] or "e+" .. tostring(floor(log10(x)))
	return format("%.".. decimals .. "f%s", x / pow(1000, exp), suffix)
end

local function AbbreviateWithNicemike(x: number, decimals: number?)
	if decimals == nil then decimals = 0 end
	
	local visible = nil
	local suffix = nil
	local places = pow(10, decimals)
	if x < 1000 then
		visible = x * places
		suffix = ""
	else
		local digits = floor(log10(x)) + 1
		local index = min(#suffixlist-1, floor((digits - 1) / 3))
		visible = x / pow(10, index * 3 - decimals)
		suffix = suffixlist[index+1]
	end
	local front = visible / places
	local back = visible % places

	if decimals > 0 then
		return format("%i.%0." .. tostring(decimals) .. "i%s", front, back, suffix)
	else
		return format("%i%s", front, suffix)
	end
end

local function check(func: ((number, number?) -> string), name: string, x: number, d: number?, e: string, alt: string?)
	local r = func(x, d)
	if r == e then return end
	if alt and r == alt then return end
	warn(format("%s(%s, %s) wrong: expected %s, got %s", name, tostring(x), d and tostring(d) or "nil", alt and e .. " or " .. alt or e, r))
end

local function test(func: ((number, number?) -> string), name)
	-- a quick correctness test
	check(func, name, 0, nil, "0")
	check(func, name, 0, 4, "0.0000")
	check(func, name, 1001, 2, "1.00k")
	check(func, name, 1001, 3, "1.001k")
	check(func, name, 100234, 2, "100.23k")
	check(func, name, 1002345678, 2, "1.00B")
	check(func, name, 1.23456789e35, nil, "123D")
	check(func, name, 1.23456789e35, 2, "123.45D", "123.46D")
	check(func, name, 1.23456789e44, 2, "123.45Trd", "123.46Trd")
	check(func, name, 1.23456789e75, 4, "1234567.8900Gp", "1.2346e+75")
	
	-- and a speed test
	local res
	local start = os.clock()
	for times = 1, 1000 do
		for x = 0, 45 do
			for d = 0, 20 do
				res = func(pow(10, x), d)
			end 
		end
	end
	local stop = os.clock()

	print(format("%s took %d ms", name, 1000 * (stop-start)))
	task.wait()
	local start2 = os.clock()
	for times = 1, 40 do
		for x = 0, 1000 do
			for d = 0, 20 do
				res = func(pow(10, x), d)
			end 
		end
	end
	local stop2 = os.clock()
	print(format("%s took %d ms (small numbers only)", name, 1000 * (stop2-start2)))
	task.wait()
end

test(AbbreviateWithIfs, "AbbreviateWithIfs")
test(AbbreviateWithFormat, "AbbreviateWithFormat")
test(AbbreviateWithNicemike, "AbbreviateWithNicemike")

They’re so incredibly close to each other. None of it matters and they’re all the same at 60 FPS. Use whichever method you find prettiest. Also mine rounds down and the others round to the nearest number.

2 Likes

Thank you for turning this into a situation which steps around the code-fighting.