Page 1 of 1
Spaces in number
Posted: Mon Feb 22, 2016 8:52 am
by feelixe
Hi! I've been thinking about a solution for this for i while but i can't come up with anything good. So, How do i format a number so there's spaces at every thousand. like this
68520 -> 68 520
1560340 -> 1 560 340
It's probably really simple but im stuck hehe. Thanks.
Re: Spaces in number
Posted: Mon Feb 22, 2016 9:33 am
by Nixola
If you're sure the number is an integer you could do it so:
Code: Select all
function spacenumber(n)
local s = string.reverse(n)
return s:gsub("(%d%d%d)", "%1 "):reverse()
end
(not tested much)
Re: Spaces in number
Posted: Mon Feb 22, 2016 11:31 am
by ivan
For very large numbers you need to disable scientific notation (note, code would work with integers ONLY) so:
Code: Select all
function spaceinteger(n)
local s = string.format("%.0f",n):reverse()
s = s:gsub("(%d%d%d)", "%1 "):reverse()
return s:gsub("^(-?) ", "%1") -- dirty fix for leading space
end
For floating point numbers you need to specify how many digits after the decimal you would like:
Code: Select all
function spacenumber(n, d)
d = d or 0
local i, f = math.modf(n)
local s = spaceinteger(i)
if d > 0 and not s:find("n") then
f = math.abs(f)
s = s .. "." .. string.format("%." .. d .. "f", f):sub(3)
end
return s
end
Alternative with pattern matching:
Code: Select all
function spacenumber(n, d)
local s = string.format("%." .. (d or 0) .. "f", n)
local n,i,f = s:match("^(-?)(%d*)(.*)$")
i = i and (i:reverse():gsub("(%d%d%d)", "%1 "):reverse()) or ''
i = i:gsub("^(-?) ", "%1")
return (n or '') .. i .. (f or '')
end
PS. Further reading:
http://lua-users.org/wiki/FormattingNumbers
PPS. Quick hack fix for negative numbers/leading space/nan and inf
Re: Spaces in number
Posted: Mon Feb 22, 2016 12:21 pm
by zorg
One possible algorithm that would work on floats would be: (in pseudocode)
- Get the number of "digits" (characters) that exist to the left of a dot (marking the decimal separator; messing with the lua locale may make this something else, though that's not really recommended)
- divide the above number by 3, and if it has a remainder, add one to it (and subtract 1, because we don't want an extra space before a number like "999")
- with a for loop starting at digitnum - floorednum and going to digitnum with an iteration step size of 3, insert a space each time until the loop terminates.
you got the number as a string, though this may not be the most efficient algorithm; one could try implementing this with string functions instead of a for loop.
Re: Spaces in number
Posted: Mon Feb 22, 2016 3:28 pm
by ivan
Zorg's version using string.sub (positive integers only):
Code: Select all
function spaceinteger(n)
local s = string.format("%.0f", n)
local f = s:len()%3
return s:sub(1, f) .. ' ' .. s:sub(f + 1):gsub("(%d%d%d)", "%1 ")
end
Re: Spaces in number
Posted: Mon Feb 22, 2016 4:33 pm
by zorg
ivan wrote:Zorg's version using string.sub (positive integers only): (...)
Here's a potential negative-allowing,
one-linered version of the above: (Though i did state that the algorithm could handle non-integers as well, but ivan's implementation can't, this one, however, can.)
Code: Select all
function spacenumber(n)
local sign = n >= 0 and '' or '-'
local abs = math.abs(n)
local str = string.format("%.18f", abs)
local delimiter = str:find('%.')
local prefix = string.sub(str, 1, delimiter-1)
local postfix = string.sub(str, delimiter , -1)
local len = prefix:len()%3; len = len > 0 and len or len-1
return sign .. prefix:sub(1,len) .. prefix:sub(len+1):gsub("(%d%d%d)"," %1") .. postfix
end
return spacenumber(-10123456.52)
Edit: not perfect yet, since numbers that have 3 significant digits get duped. (e.g. 101.whatever becomes 101 101.whatever)
Re: Spaces in number
Posted: Wed Feb 24, 2016 5:57 pm
by airstruck
Thought I'd take a crack at this.
Code: Select all
local function separate (n, sep)
local prefix, suffix, previous = tostring(n):match '^(.%d*)(.*)$'
local pattern, repl = '(%d)(%d%d%d%f[%D])', '%1' .. sep .. '%2'
while prefix ~= previous do
previous = prefix
prefix = prefix:gsub(pattern, repl, 1)
end
return prefix .. suffix
end
Should work with negative and positive integers and fractional numbers. Shouldn't change special representations like scientific notation, "nan" and "infinity".
Algorithm is basically:
1 - Separate number into integer representation and fractional representation.
2 - In the integer representation, find a sequence of four digits not followed by another digit.
3 - If found, insert separator between first and second digits, update integer representation with the result, go to step 2.
4 - Else not found, return integer representation concatenated to fractional representation.
Here's a short solution that's not iterative.
Code: Select all
local function separate (n, sep)
local s, i, f = tostring(n):match '^(%D?)(%d*)(.*)$'
local p = (#i - 1) % 3 + 1
return s .. i:sub(1, p) .. i:sub(p + 1):gsub('%d%d%d', sep .. '%1') .. f
end
The idea here is to skip 1 to 3 leading digits in the integral part, then put a separator before each run of three digits after that.