Remove Trailing Zeros

Posted by Xvordan on Wed 07 Oct 2015 01:58 PM — 8 posts, 33,708 views.

#0
I'm playing a mud which uses inhumanely huge numbers. In an effort to curtail the onslaught of numbers on my screen, I wrote a function that converts values like 8,000,000,000 into "8 billion".

I'm using a division method, and it works well when paired with string.format to narrow the result down to only one decimal place, like 8.3 billion. My problem is that string.format also likes to make a whole number a decimal, so 100 billion becomes 100.0 billion.

I tried fixing it with string.gsub(string, "%.0", ""), but it didn't seem to do anything to the results -- I'm still getting 100.0 million. Anyone know of a good solution? I really don't want to do math.floor or rounding it down or anything like that, since if there is a useful decimal like 8.6, I want to keep it.

Function follows:


function truncate (s_trunc)
s_trunc = string.gsub(s_trunc, ",", "")
n_trunc = tonumber(s_trunc)
if (string.len(s_trunc) >= 1) and (string.len(s_trunc) <= 3) then
return s_trunc
elseif (string.len(s_trunc) >= 4) and (string.len(s_trunc) <= 6) then
n_divide = 1000
n_class = "thousand"
elseif (string.len(s_trunc) >= 7) and (string.len(s_trunc) <= 9) then
n_divide = 1000000
n_class = "million"
elseif (string.len(s_trunc) >= 10) and (string.len(s_trunc) <= 12) then
n_divide = 1000000000
n_class = "billion"
elseif (string.len(s_trunc) >= 13) and (string.len(s_trunc) <= 15) then
n_divide = 1000000000000
n_class = "trillion"
elseif (string.len(s_trunc) >= 16) and (string.len(s_trunc) <= 18) then
n_divide = 1000000000000000
n_class = "quadrillion"
elseif (string.len(s_trunc) >= 19) and (string.len(s_trunc) <= 21) then
n_divide = 1000000000000000000
n_class = "quintillion"
elseif (string.len(s_trunc) >= 22) and (string.len(s_trunc) <= 24) then
n_divide = 1000000000000000000000
n_class = "sextillion"
elseif (string.len(s_trunc) >= 25) and (string.len(s_trunc) <= 27) then
n_divide = 1000000000000000000000000
n_class = "septillion"
elseif (string.len(s_trunc) >= 28) and (string.len(s_trunc) <= 30) then
n_divide = 1000000000000000000000000000
n_class = "octillion"
else
return "error"
end
return string.gsub(string.format("%.1f", n_trunc/n_divide), "%.0", "").." "..n_class
end
Australia Forum Administrator #1
See http://www.gammon.com.au/forum/?id=10155


Anyway, modifying your idea somewhat this seems to work:


function truncate (str)
  str = string.gsub(str, ",", "")  -- get rid of commas
  len = string.len (string.match (str, "%d+"))  -- find size of integer portion
  if (len >= 1) and (len <= 3) then
    return str
  elseif (len >= 4) and (len <= 6) then
    divideBy = 1E3
    numberSize = "thousand"
  elseif (len >= 7) and (len <= 9) then
    divideBy = 1E6
    numberSize = "million"
  elseif (len >= 10) and (len <= 12) then
    divideBy = 1E9
    numberSize = "billion"
  elseif (len >= 13) and (len <= 15) then
    divideBy = 1E12
    numberSize = "trillion"
  elseif (len >= 16) and (len <= 18) then
    divideBy = 1E15
    numberSize = "quadrillion"
  elseif (len >= 19) and (len <= 21) then
    divideBy = 1E18
    numberSize = "quintillion"
  elseif (len >= 22) and (len <= 24) then
    divideBy = 1E21
    numberSize = "sextillion"
  elseif (len >= 25) and (len <= 27) then
    divideBy = 1E24
    numberSize = "septillion"
  elseif (len >= 28) and (len <= 30) then
    divideBy = 1E27
    numberSize = "octillion"
  else
    return "number error: " .. str
  end
  -- divide down, convert to float, omit trailing ".0", show one decimal place
  return string.gsub(string.format("%.1f", tonumber(str)/divideBy), "%.0", "") .. " " .. numberSize
end


print (truncate ("8,600,000,000.22"))
print (truncate ("1234.567"))
print (truncate ("123.456"))
print (truncate ("8,000,000,000."))


Output:


8.6 billion
1.2 thousand
123.456
8 billion
#2
Thanks Nick! I'm not sure why your version worked and mine didn't, since I think that at the final step, ours looked fairly similar, and I'm not actually sure what I did wrong to make gsub not strip out the trailing 0's.

A problem with string.format, though: it likes to round up, converting 999999.999 to 1 million, so I end up with things like 1000 thousand when I truncate that number, which I'm sure looks strange to more than just me. Is there any way to keep it from rounding, and just displaying the number as 999999.9?
USA Global Moderator #3
If you don't want it to round, try subtracting sign*0.05 from your number first.
Australia Forum Administrator #4
The "fix" was that I took the length of the integer portion (third line).

As for the rounding, Fiendish's suggestion seems to work:


  return string.gsub(string.format("%.1f", tonumber(str)/divideBy - 0.05), "%.0", "") .. " " .. numberSize


Although that might fail for small numbers. You might want to conditionally do it, depending on how big "len" is.

Actually, for small numbers, the divide is bypassed, so that could be OK.
Amended on Thu 08 Oct 2015 07:54 PM by Nick Gammon
#5
Thanks Fiendish and Nick. You guys have been amazingly helpful. Though, I discovered that working with numbers in this way isn't always the best, since Lua doesn't like such huge numbers anyway. I just ran across the pages talking about the BC library, so I'm gonna have a go at using bc to help me make this work even better. I, for example, can't do any operations on the values I extrapolate from the mud, since about 90% of the numbers this mud uses are over the 2^52 limit. For example, my experience is now 104,906,932,793,409,552,121,856, and I need to see what x times that is to get to my next point. lol. Don't be surprised to hear from me again with BC, though. I really am not great with math.

Kai
USA Global Moderator #6
I would just not tonumber the whole thing. You only ever need to look at the first three to five digits.

Your numeric output is essentially following the format of: "first_one_to_three_digits." followed by math.floor(next_digit.next_digit + 0.5)

You can easily check if that following part is zero before appending it.
Amended on Sat 10 Oct 2015 11:00 PM by Fiendish
Australia Forum Administrator #7
The BC library can certainly handle large numbers like that, once you remove the commas.