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.