Register forum user name Search FAQ

Gammon Forum

Notice: Any messages purporting to come from this site telling you that your password has expired, or that you need to verify your details, confirm your email, resolve issues, making threats, or asking for money, are spam. We do not email users with any such messages. If you have lost your password you can obtain a new one by using the password reset link.

Due to spam on this forum, all posts now need moderator approval.

 Entire forum ➜ MUSHclient ➜ Lua ➜ Converting numbers from text and vice-versa

Converting numbers from text and vice-versa

It is now over 60 days since the last post. This thread is closed.     Refresh page


Pages: 1  2 3  4  

Posted by Tiopon   USA  (71 posts)  Bio
Date Reply #15 on Wed 17 Mar 2010 07:55 PM (UTC)

Amended on Wed 17 Mar 2010 07:59 PM (UTC) by Tiopon

Message
The text is formatted all lower case with spaces (not hyphens) between the words. So 85 would be rendered as eighty five currently.

Making it work for me is fairly easy... I just thought making something less hack-ish would likely be useful for others in a similar way to the trigger and alias toggle aliases I posted filled some needs that hadn't been done as simply before.

Obviously in my case, I can just make it match on the words in a do while loop. Parsing two words at a time would allow for doing it fairly easily, but it's not as expandable, and anyone else who may need this in the future won't be able to use it...

Talking myself through that sentence made me ponder something... If I have a do while loop running that pulls the next word and checks it for an additive sense... so if the current word is eighty and the next word is five, consider the words together... if the following word is hundred, multiply the whole mass by 100, if it's not or it ends, just splice together the answers... Might work if it's done right. Still has possible problems with numbers like Nick mentioned, though that depends on how I choose to do the parsing. Probably have it so that if there's a modifier, it will take another larger modifier, but if the next modifier is smaller to consider it to be a new entry.

Should I pursue this, or does it sound like I just hit a major useless rabbit trail? :)

Sorry, just saw your note David... does that mean to use that, I'd need to include entries from 0-99 done in text, then it would decipher it...? How well does that work with non-hyphenated numbers, would it take eighty five thousand as 80+5000 or as 85000?
Top

Posted by Nick Gammon   Australia  (23,133 posts)  Bio   Forum Administrator
Date Reply #16 on Wed 17 Mar 2010 08:09 PM (UTC)
Message
No-one has mentioned it so far, so I will ...

You will need to use the "bc" library in MUSHclient (assuming this is somehow related to MUSHclient). And if it isn't, the library is available stand-alone.

The "bc" library lets you work with arbitrarily large numbers.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by David Haley   USA  (3,881 posts)  Bio
Date Reply #17 on Wed 17 Mar 2010 08:32 PM (UTC)
Message
I would implement this along the following lines, looping over words one at a time.

The stack is initially empty.

If the current word is a number (English or numeric), and the previous word was also a number, pop the stack and push the addition. Otherwise, push the number.

If the current word is a unit, multiply the number on the top of the stack by the unit's magnitude. (It is an error to get a unit before a number.)

When the input has been parsed, sum all numbers on the stack.

E.g...


current word             stack
fifty                    50
three                    53 <pop 50, add 3, push 53>
million                  53000000 <pop 53, *1M>
billion                  53000000000000000
eighty                   80, 53000000000000000
million                  80000000, 53000000000000000
forty                    40, 80000000, 53000000000000000
one                      41, 80000000, 53000000000000000
thousand                 41000, 80000000, 53000000000000000
six                      6, 41000, 80000000, 53000000000000000
hundred                  600, 41000, 80000000, 53000000000000000
twenty                   20, 600, 41000, 80000000, 53000000000000000
two                      22, 600, 41000, 80000000, 53000000000000000


and then you sum those numbers, and get the same result discussed previously.

The only difficulty here is in constructing the list of English number strings and their equivalence in numeric values. But since you're breaking apart values, it's not so bad: you only need the following:

one two three four five six seven eight nine ten eleven twelve thirteen fourteen fifteen sixteen seventeen eighteen nineteen twenty thirty forty fifty sixty seventy eighty ninety

David Haley aka Ksilyan
Head Programmer,
Legends of the Darkstone

http://david.the-haleys.org
Top

Posted by Nick Gammon   Australia  (23,133 posts)  Bio   Forum Administrator
Date Reply #18 on Wed 17 Mar 2010 10:07 PM (UTC)

Amended on Thu 18 Mar 2010 10:05 PM (UTC) by Nick Gammon

Message
Something like this, David?

[EDIT] This is wrong. See below for better version.


[EDIT] See improved version on page 4:
http://www.gammon.com.au/forum/?id=10155&page=4




-- DON'T USE THIS -- see amended version below!!!

numbers = {
         one        = bc.number (1),
         two        = bc.number (2),
         three      = bc.number (3),
         four       = bc.number (4),
         five       = bc.number (5),
         six        = bc.number (6),
         seven      = bc.number (7),
         eight      = bc.number (8),
         nine       = bc.number (9),
         ten        = bc.number (10),
         eleven     = bc.number (11),
         twelve     = bc.number (12),
         thirteen   = bc.number (13),
         fourteen   = bc.number (14),
         fifteen    = bc.number (15),
         sixteen    = bc.number (16),
         seventeen  = bc.number (17),
         eighteen   = bc.number (18),
         nineteen   = bc.number (19),
         twenty     = bc.number (20),
         thirty     = bc.number (30),
         forty      = bc.number (40),
         fifty      = bc.number (50),
         sixty      = bc.number (60),
         seventy    = bc.number (70),
         eighty     = bc.number (80),
         ninety     = bc.number (90),
} -- numbers 

units = {
          hundred      = bc.number ("100"),
          thousand     = bc.number ("1000"),
          million      = bc.number ("1000000"),
          billion      = bc.number ("1000000000"),
          trillion     = bc.number ("1000000000000"),
          quadrillion  = bc.number ("1000000000000000"),
          quintillion  = bc.number ("1000000000000000000"),
          sextillion   = bc.number ("1000000000000000000000"),
          septillion   = bc.number ("1000000000000000000000000"),
          octillion    = bc.number ("1000000000000000000000000000"),
          nonillion    = bc.number ("1000000000000000000000000000000"),
          decillion    = bc.number ("1000000000000000000000000000000000"),
          undecillion  = bc.number ("1000000000000000000000000000000000000"),
          duodecillion = bc.number ("1000000000000000000000000000000000000000"),
  } -- units
  
-- convert a number in words to a numeric form
-- See: http://www.gammon.com.au/forum/?id=10155
-- Thanks to David Haley

function convert (s)

  local stack = {}
  local previous_type
  
  s = string.gsub (s:lower (), "%-", " ")  -- convert hyphens to spaces
  
  for word in string.gmatch (s, "%a+") do
    if word ~= "and" then  -- skip "and" (like "hundred and fifty two")
      local top = #stack
      
      -- If the current word is a number (English or numeric), and the previous word 
      --    was also a number, pop the stack and push the addition. 

      -- Otherwise, push the number.

      local number = numbers [word]
      if number then
        if previous_type == "number" then   -- eg. forty three
          local previous_number = table.remove (stack, top)  -- get the three
          number = number + previous_number  -- add forty
        end -- if 
        table.insert (stack, number)   
        previous_type = "number"
      else
      
        -- If the current word is a unit, multiply the number on the top of the stack by the unit's magnitude. 
        local unit = units [word]
        if not unit then
          return nil, "Unexpected word: " .. word
        end -- not unit
        previous_type = "unit"
        
        -- It is an error to get a unit before a number.
        
        if top == 0 then
          return nil, "Cannot have unit before a number: " .. word
        end -- starts of with something like "thousand"
        stack [top] = stack [top] * unit   
       
      end -- if number or not
    end -- if 'and'
  end -- for each word
  
  if #stack == 0 then
    return nil, "No number found"
  end -- nothing
  
  -- When the input has been parsed, sum all numbers on the stack.
  
  local result = bc.number (0)
  for _, item in ipairs (stack) do
    result = result + item
  end -- for
  
  return result
end -- function convert

test = " fifty-three million billion eighty million forty-one thousand six hundred and twenty-two"

n = assert (convert (test))
print ("Result =", n, "length =", #bc.tostring (n))


Output:


Result = 53000000080041622 length = 17


You could put alternatives in the units list above or add others as you require (eg. septdecillion, septendecillion). You might want to add plurals (eg. "hundreds", "thousands").

Note that this algorithm does not handle decimal places. Nor has it been extensively tested.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Nick Gammon   Australia  (23,133 posts)  Bio   Forum Administrator
Date Reply #19 on Wed 17 Mar 2010 10:20 PM (UTC)
Message
It fails on:

"two trillion four hundred thirty-two billion eight hundred seventy-six million three hundred forty-five thousand nine hundred twenty-three"

It should give: 2,432,876,345,923

But actually gives: 2,032,076,047,423

So don't use it yet.

(It will also fail on "zero").

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Nick Gammon   Australia  (23,133 posts)  Bio   Forum Administrator
Date Reply #20 on Wed 17 Mar 2010 10:28 PM (UTC)

Amended on Wed 17 Mar 2010 10:30 PM (UTC) by Nick Gammon

Message
This is the problem:


Converting: two trillion four hundred thirty-two billion eight hundred seventy-six million three hundred forty-five thousand nine hundred twenty-three



Processing word:        two stack is: 2
Processing word:   trillion stack is: 2000000000000
Processing word:       four stack is: 2000000000000, 4
Processing word:    hundred stack is: 2000000000000, 400
Processing word:     thirty stack is: 2000000000000, 400, 30
Processing word:        two stack is: 2000000000000, 400, 32
Processing word:    billion stack is: 2000000000000, 400, 32000000000
Processing word:      eight stack is: 2000000000000, 400, 32000000000, 8
Processing word:    hundred stack is: 2000000000000, 400, 32000000000, 800
Processing word:    seventy stack is: 2000000000000, 400, 32000000000, 800, 70
Processing word:        six stack is: 2000000000000, 400, 32000000000, 800, 76
Processing word:    million stack is: 2000000000000, 400, 32000000000, 800, 76000000
Processing word:      three stack is: 2000000000000, 400, 32000000000, 800, 76000000, 3
Processing word:    hundred stack is: 2000000000000, 400, 32000000000, 800, 76000000, 300
Processing word:      forty stack is: 2000000000000, 400, 32000000000, 800, 76000000, 300, 40
Processing word:       five stack is: 2000000000000, 400, 32000000000, 800, 76000000, 300, 45
Processing word:   thousand stack is: 2000000000000, 400, 32000000000, 800, 76000000, 300, 45000
Processing word:       nine stack is: 2000000000000, 400, 32000000000, 800, 76000000, 300, 45000, 9
Processing word:    hundred stack is: 2000000000000, 400, 32000000000, 800, 76000000, 300, 45000, 900
Processing word:     twenty stack is: 2000000000000, 400, 32000000000, 800, 76000000, 300, 45000, 900, 20
Processing word:      three stack is: 2000000000000, 400, 32000000000, 800, 76000000, 300, 45000, 900, 23
Result = 2032076047423 length = 13


Processing "four hundred thirty-two billion" it is not associating the "four hundred" with the "thirty-two billion".

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Nick Gammon   Australia  (23,133 posts)  Bio   Forum Administrator
Date Reply #21 on Wed 17 Mar 2010 10:57 PM (UTC)

Amended on Thu 18 Mar 2010 10:06 PM (UTC) by Nick Gammon

Message
OK I think I have it. This is better:

[EDIT] Amended to allow for "real" numbers (eg. "42 million 500 thousand")


[EDIT] Amended to allow for the input zero.

[EDIT] See improved version on page 4:
http://www.gammon.com.au/forum/?id=10155&page=4



-- Convert words to a number
-- Author: Nick Gammon
-- Date: 18th March 2010

-- Does NOT handle decimal places (eg. four point six)

local numbers = {
         zero       = bc.number (0),
         one        = bc.number (1),
         two        = bc.number (2),
         three      = bc.number (3),
         four       = bc.number (4),
         five       = bc.number (5),
         six        = bc.number (6),
         seven      = bc.number (7),
         eight      = bc.number (8),
         nine       = bc.number (9),
         ten        = bc.number (10),
         eleven     = bc.number (11),
         twelve     = bc.number (12),
         thirteen   = bc.number (13),
         fourteen   = bc.number (14),
         fifteen    = bc.number (15),
         sixteen    = bc.number (16),
         seventeen  = bc.number (17),
         eighteen   = bc.number (18),
         nineteen   = bc.number (19),
         twenty     = bc.number (20),
         thirty     = bc.number (30),
         forty      = bc.number (40),
         fifty      = bc.number (50),
         sixty      = bc.number (60),
         seventy    = bc.number (70),
         eighty     = bc.number (80),
         ninety     = bc.number (90),
} -- numbers 

local units = {
          hundred      = bc.number ("100"),
          thousand     = bc.number ("1000"),
          million      = bc.number ("1000000"),
          billion      = bc.number ("1000000000"),
          trillion     = bc.number ("1000000000000"),
          quadrillion  = bc.number ("1000000000000000"),
          quintillion  = bc.number ("1000000000000000000"),
          sextillion   = bc.number ("1000000000000000000000"),
          septillion   = bc.number ("1000000000000000000000000"),
          octillion    = bc.number ("1000000000000000000000000000"),
          nonillion    = bc.number ("1000000000000000000000000000000"),
          decillion    = bc.number ("1000000000000000000000000000000000"),
          undecillion  = bc.number ("1000000000000000000000000000000000000"),
          duodecillion = bc.number ("1000000000000000000000000000000000000000"),
  } -- units
  
-- convert a number in words to a numeric form
-- See: http://www.gammon.com.au/forum/?id=10155
-- Thanks to David Haley

function convert_words_to_numbers (s)

  local stack = {}
  local previous_type
 
  for word in string.gmatch (s:lower (), "[%a%d]+") do
    if word ~= "and" then  -- skip "and" (like "hundred and fifty two")
      local top = #stack
      
      -- If the current word is a number (English or numeric), and the previous word was also a number, pop the stack and push the addition. 
      -- Otherwise, push the number.

      local number = tonumber (word)  -- try for numeric (eg. 22 thousand)

      if number then
        number = bc.number (number)   -- turn into "big number"
      else
        number = numbers [word]
      end -- if a number-word "like: twenty"

      if number then
        if previous_type == "number" then   -- eg. forty three
          local previous_number = table.remove (stack, top)  -- get the three
          number = number + previous_number  -- add forty
        end -- if 
        table.insert (stack, number)   
        previous_type = "number"
      else
      
        -- If the current word is a unit, multiply the number on the top of the stack by the unit's magnitude. 
        local unit = units [word]
        if not unit then
          return nil, "Unexpected word: " .. word
        end -- not unit
        previous_type = "unit"
        
        -- It is an error to get a unit before a number.
        
        if top == 0 then
          return nil, "Cannot have unit before a number: " .. word
        end -- starts of with something like "thousand"

        -- pop until we get something larger on the stack
        local interim_result = bc.number (0)
        while top > 0 and stack [top] < unit do
          interim_result = interim_result + table.remove (stack, top)
          top = #stack
        end -- while
        table.insert (stack, interim_result * unit)
               
      end -- if number or not
    end -- if 'and'

  end -- for each word
  
  if #stack == 0 then
    return nil, "No number found"
  end -- nothing
  
  -- When the input has been parsed, sum all numbers on the stack.
  
  local result = bc.number (0)
  for _, item in ipairs (stack) do
    result = result + item
  end -- for
  
  return result
end -- function convert_words_to_numbers


tests = { [bc.number ("2432876345923")] = "two trillion four hundred thirty-two billion eight hundred seventy-six million three hundred forty-five thousand nine hundred twenty-three", [bc.number ("44240592621")] = "forty-four billion two hundred forty million five hundred ninety-two thousand six hundred twenty-one", [bc.number ("277198501535")] = "two hundred seventy-seven billion one hundred ninety-eight million five hundred one thousand five hundred thirty-five", [bc.number ("477708080833")] = "four hundred seventy-seven billion seven hundred eight million eighty thousand eight hundred thirty-three", [bc.number ("67707562873705")] = "sixty-seven trillion seven hundred seven billion five hundred sixty-two million eight hundred seventy-three thousand seven hundred five", [bc.number ("67707562873705")] = "67 trillion 7 hundred seven billion 5 hundred 62 million eight hundred 73 thousand 705", } for number, words in pairs (tests) do print ("Converting: ", words) n = assert (convert_words_to_numbers (words)) assert (number == n, "Conversion failed!") print ("Result =", n, "length =", #bc.tostring (n)) end -- for


Output:


Converting:  sixty-seven trillion seven hundred seven billion five hundred sixty-two million eight hundred seventy-three thousand seven hundred five
Result = 67707562873705 length = 14
Converting:  forty-four billion two hundred forty million five hundred ninety-two thousand six hundred twenty-one
Result = 44240592621 length = 11
Converting:  two trillion four hundred thirty-two billion eight hundred seventy-six million three hundred forty-five thousand nine hundred twenty-three
Result = 2432876345923 length = 13
Converting:  67 trillion 7 hundred seven billion 5 hundred 62 million eight hundred 73 thousand 705
Result = 67707562873705 length = 14
Converting:  two hundred seventy-seven billion one hundred ninety-eight million five hundred one thousand five hundred thirty-five
Result = 277198501535 length = 12
Converting:  four hundred seventy-seven billion seven hundred eight million eighty thousand eight hundred thirty-three
Result = 477708080833 length = 12


When the "units" arrive (eg. billion) you don't just pop the last number, you pop until you get something larger than a billion.

So in the example earlier, "four hundred thirty-two billion" ... when you get the "billion" you keep popping (and adding) until we reach the "two trillion" number, which is larger than the "billion" unit.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Nick Gammon   Australia  (23,133 posts)  Bio   Forum Administrator
Date Reply #22 on Wed 17 Mar 2010 11:00 PM (UTC)
Message
Maybe the input "zero" is a special case, after all you don't normally say "twenty-two and zero".

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Tiopon   USA  (71 posts)  Bio
Date Reply #23 on Thu 18 Mar 2010 12:08 AM (UTC)
Message
I just put zero at the top of the listed numbers, above one... so the top section looks like:
local numbers = {
         zero       = bc.number (0),
         one        = bc.number (1),
         two        = bc.number (2),
         three      = bc.number (3),
and that seems to work properly for those cases... any possible caveats on that?
Top

Posted by Nick Gammon   Australia  (23,133 posts)  Bio   Forum Administrator
Date Reply #24 on Thu 18 Mar 2010 01:03 AM (UTC)
Message
Oh well, if it works, it works.

I amended by post to have that in it.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Nick Gammon   Australia  (23,133 posts)  Bio   Forum Administrator
Date Reply #25 on Thu 18 Mar 2010 01:29 AM (UTC)

Amended on Thu 18 Mar 2010 10:06 PM (UTC) by Nick Gammon

Message
For completeness, I did the inverse function as well.

[EDIT] See improved version on page 4:
http://www.gammon.com.au/forum/?id=10155&page=4



-- Convert a number to words
-- Author: Nick Gammon
-- Date: 18th March 2010

local inverse_units = {
  {  len = #"1000000000000000000000000000000000000000",    word = "duodecillion " },
  {  len = #"1000000000000000000000000000000000000",       word = "undecillion " },
  {  len = #"1000000000000000000000000000000000",          word = "decillion " },
  {  len = #"1000000000000000000000000000000",             word = "nonillion " },
  {  len = #"1000000000000000000000000000",                word = "octillion " },
  {  len = #"1000000000000000000000000",                   word = "septillion " },
  {  len = #"1000000000000000000000",                      word = "sextillion " },
  {  len = #"1000000000000000000",                         word = "quintillion " },
  {  len = #"1000000000000000",                            word = "quadrillion " },
  {  len = #"1000000000000",                               word = "trillion " },
  {  len = #"1000000000",                                  word = "billion " },
  {  len = #"1000000",                                     word = "million " },
  {  len = #"1000",                                        word = "thousand " },
  } -- inverse_units
  
  
local inverse_numbers = {
    "one ",
    "two ",
    "three ",
    "four ",
    "five ",
    "six ",
    "seven ",
    "eight ",
    "nine ",
    "ten ",
    "eleven ",
    "twelve ",
    "thirteen ",
    "fourteen ",
    "fifteen ",
    "sixteen ",
    "seventeen ",
    "eighteen ",
    "nineteen ",
    "twenty ",
    [30] = "thirty ",
    [40] = "forty ",
    [50] = "fifty ",
    [60] = "sixty ",
    [70] = "seventy ",
    [80] = "eighty ",
    [90] = "ninety ",
 }  -- inverse_numbers
 

-- convert a number in words to a numeric form
-- See: http://www.gammon.com.au/forum/?id=10155

local function convert_up_to_999 (n)

  if n <= 0 then
    return ""
  end -- if zero
  
  local hundreds = math.floor (n / 100)
  local tens = math.floor (n % 100)
  local result = ""

  -- if over 99 we need to say x hundred  
  if hundreds > 0 then
   result = inverse_numbers [hundreds] .. "hundred "
  end -- if
  
  if tens == 0 then
    return result
  end -- if only a digit in the hundreds column
  
  -- up to twenty it is then just five hundred (and) fifteen
  if tens <= 20 then
    return result .. inverse_numbers [tens] 
  end -- if

  -- otherwise we need: thirty (something)
  result = result .. inverse_numbers [math.floor (tens / 10) * 10] 
  
  -- get final digit (eg. thirty four)
  local digits = math.floor (n % 10)

  if digits > 0 then
    result = result ..  inverse_numbers [digits]
  end -- if 
  
  return result
  
end -- convert_up_to_999

function convert_numbers_to_words (n)
  local s = tostring (n)
  -- make multiple of 3
  while #s % 3 > 0 do
    s = "0" .. s
  end -- while
  
  local result = ""
  local start = #inverse_units - #s / 3 + 2
  
  for i = start, #inverse_units do
    local group = tonumber (string.sub (s, 1, 3))
    if group > 0 then
      result = result .. convert_up_to_999 (group) .. inverse_units [i].word
    end -- if not zero
    s = string.sub (s, 4)    
  end -- for
  
  result = result .. convert_up_to_999 (tonumber (s)) 

  if result == "" then
    result = "zero"
  end -- if
  
  return Trim (result)

end -- convert_numbers_to_words


tests = { [bc.number ("542")] = "five hundred forty two", [bc.number ("2432876345923")] = "two trillion four hundred thirty two billion eight hundred seventy six million three hundred forty five thousand nine hundred twenty three", [bc.number ("44240592621")] = "forty four billion two hundred forty million five hundred ninety two thousand six hundred twenty one", [bc.number ("277198501535")] = "two hundred seventy seven billion one hundred ninety eight million five hundred one thousand five hundred thirty five", [bc.number ("477708080833")] = "four hundred seventy seven billion seven hundred eight million eighty thousand eight hundred thirty three", [bc.number ("67707562873705")] = "sixty seven trillion seven hundred seven billion five hundred sixty two million eight hundred seventy three thousand seven hundred five", } for number, words in pairs (tests) do print ("Converting: ", number) w = assert (convert_numbers_to_words (number)) print ("Result = ", w) print ("Expected = ", words) assert (words == w, "Conversion failed!") end -- for


Test results:


Converting:  477708080833
Result   =  four hundred seventy seven billion seven hundred eight million eighty thousand eight hundred thirty three
Expected =  four hundred seventy seven billion seven hundred eight million eighty thousand eight hundred thirty three
Converting:  2432876345923
Result   =  two trillion four hundred thirty two billion eight hundred seventy six million three hundred forty five thousand nine hundred twenty three
Expected =  two trillion four hundred thirty two billion eight hundred seventy six million three hundred forty five thousand nine hundred twenty three
Converting:  277198501535
Result   =  two hundred seventy seven billion one hundred ninety eight million five hundred one thousand five hundred thirty five
Expected =  two hundred seventy seven billion one hundred ninety eight million five hundred one thousand five hundred thirty five
Converting:  67707562873705
Result   =  sixty seven trillion seven hundred seven billion five hundred sixty two million eight hundred seventy three thousand seven hundred five
Expected =  sixty seven trillion seven hundred seven billion five hundred sixty two million eight hundred seventy three thousand seven hundred five
Converting:  542
Result   =  five hundred forty two
Expected =  five hundred forty two
Converting:  44240592621
Result   =  forty four billion two hundred forty million five hundred ninety two thousand six hundred twenty one
Expected =  forty four billion two hundred forty million five hundred ninety two thousand six hundred twenty one

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Nick Gammon   Australia  (23,133 posts)  Bio   Forum Administrator
Date Reply #26 on Thu 18 Mar 2010 01:32 AM (UTC)
Message
And if you put both lots of functions together, and make a little test driver:


print (string.rep ("-", 75))

for i = 1, 5000 do
  local s = ""
  for i = 1, math.floor (MtRand () * 40 + 1) do
    s = s .. math.floor (MtRand () * 10)
  end -- for
  
  print ("Converting: '" .. s .. "'")
  local words = assert (convert_numbers_to_words (s))
  print ("Result =", words)
  n = assert (convert_words_to_numbers (words))
  print ("Result    : '" .. n:tostring () .. "'", "length =", #bc.tostring (n))
  if not bc.iszero (n) then
    assert (string.gsub (s, "^0+", "") == n:tostring (), "Conversion failed!")
  end -- if
end -- for



That ran to completion and demonstrated that 5000 randomly-generated numbers could be converted into words and then back again to the same number.

A small sample of its output:



Converting: '1191160927'
Result = one billion one hundred ninety one million one hundred sixty thousand nine hundred twenty seven
Result : '1191160927' length = 10
Converting: '87158213994542611069177258822761676523'
Result = eighty seven undecillion one hundred fifty eight decillion two hundred thirteen nonillion nine hundred ninety four octillion five hundred forty two septillion six hundred eleven sextillion sixty nine quintillion one hundred seventy seven quadrillion two hundred fifty eight trillion eight hundred twenty two billion seven hundred sixty one million six hundred seventy six thousand five hundred twenty three
Result : '87158213994542611069177258822761676523' length = 38
Converting: '432153673017442616601652256641810'
Result = four hundred thirty two nonillion one hundred fifty three octillion six hundred seventy three septillion seventeen sextillion four hundred forty two quintillion six hundred sixteen quadrillion six hundred one trillion six hundred fifty two billion two hundred fifty six million six hundred forty one thousand eight hundred ten
Result : '432153673017442616601652256641810' length = 33
Converting: '3651996842759435382125'
Result = three sextillion six hundred fifty one quintillion nine hundred ninety six quadrillion eight hundred forty two trillion seven hundred fifty nine billion four hundred thirty five million three hundred eighty two thousand one hundred twenty five
Result : '3651996842759435382125' length = 22
Converting: '99222052564443194'
Result = ninety nine quadrillion two hundred twenty two trillion fifty two billion five hundred sixty four million four hundred forty three thousand one hundred ninety four
Result : '99222052564443194' length = 17
Converting: '4'
Result = four
Result : '4' length = 1
Converting: '23514160057930735976287957833'
Result = twenty three octillion five hundred fourteen septillion one hundred sixty sextillion fifty seven quintillion nine hundred thirty quadrillion seven hundred thirty five trillion nine hundred seventy six billion two hundred eighty seven million nine hundred fifty seven thousand eight hundred thirty three
Result : '23514160057930735976287957833' length = 29
Converting: '9066868777537465942113528'
Result = nine septillion sixty six sextillion eight hundred sixty eight quintillion seven hundred seventy seven quadrillion five hundred thirty seven trillion four hundred sixty five billion nine hundred forty two million one hundred thirteen thousand five hundred twenty eight
Result : '9066868777537465942113528' length = 25
Converting: '327200486'
Result = three hundred twenty seven million two hundred thousand four hundred eighty six
Result : '327200486' length = 9
Converting: '587231717506879941619'
Result = five hundred eighty seven quintillion two hundred thirty one quadrillion seven hundred seventeen trillion five hundred six billion eight hundred seventy nine million nine hundred forty one thousand six hundred nineteen
Result : '587231717506879941619' length = 21
Converting: '80'
Result = eighty
Result : '80' length = 2
Converting: '5621235270740331495746400597814797'
Result = five decillion six hundred twenty one nonillion two hundred thirty five octillion two hundred seventy septillion seven hundred forty sextillion three hundred thirty one quintillion four hundred ninety five quadrillion seven hundred forty six trillion four hundred billion five hundred ninety seven million eight hundred fourteen thousand seven hundred ninety seven
Result : '5621235270740331495746400597814797' length = 34
Converting: '1792339879271350525'
Result = one quintillion seven hundred ninety two quadrillion three hundred thirty nine trillion eight hundred seventy nine billion two hundred seventy one million three hundred fifty thousand five hundred twenty five
Result : '1792339879271350525' length = 19
Converting: '8947516767792286447107637168487510769000'
Result = eight duodecillion nine hundred forty seven undecillion five hundred sixteen decillion seven hundred sixty seven nonillion seven hundred ninety two octillion two hundred eighty six septillion four hundred forty seven sextillion one hundred seven quintillion six hundred thirty seven quadrillion one hundred sixty eight trillion four hundred eighty seven billion five hundred ten million seven hundred sixty nine thousand
Result : '8947516767792286447107637168487510769000' length = 40


- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by WillFa   USA  (525 posts)  Bio
Date Reply #27 on Thu 18 Mar 2010 02:36 AM (UTC)

Amended on Thu 18 Mar 2010 03:40 AM (UTC) by WillFa

Message
I may be late to the party (it's St. Patrick's day. I'm in Boston. So.... Pub crawl!)

But here's a function in LPEG that I did.


...Code's on Page 3. I'm tired of editing this post :),,,


This doesn't do decimals. It will do stupid wordings of numbers too!

WordtoNum("one hundred seven") -> 107
WordtoNum("seven hundred thirty one thousand nine hundred forty six") -> 731946
WordtoNum("four thousand twelve hundred") -> 5200
Top

Posted by Nick Gammon   Australia  (23,133 posts)  Bio   Forum Administrator
Date Reply #28 on Thu 18 Mar 2010 02:54 AM (UTC)
Message

/print (WordtoNum  "eight hundred sixty six million five hundred thirty six thousand nine hundred eighteen")

--> 806


Afraid not.

Your solution looks elegant though. I thought an LPEG solution could be made to work.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Nick Gammon   Australia  (23,133 posts)  Bio   Forum Administrator
Date Reply #29 on Thu 18 Mar 2010 03:01 AM (UTC)
Message
If you want the word "and" in there, and hyphens between "forty" and "two" (ie. forty-two) change convert_up_to_999 to read:


function convert_up_to_999 (n)

  if n <= 0 then
    return ""
  end -- if zero
  
  local hundreds = math.floor (n / 100)
  local tens = math.floor (n % 100)
  local result = ""

  -- if over 99 we need to say x hundred  
  if hundreds > 0 then
    result = inverse_numbers [hundreds] .. "hundred "
    if tens == 0 then
      return result
    end -- if only a digit in the hundreds column
  
    result = result .. "and "

  end -- if
  

  -- up to twenty it is then just five hundred (and) fifteen
  if tens <= 20 then
    return result .. inverse_numbers [tens] 
  end -- if

  -- otherwise we need: thirty (something)
  result = result .. inverse_numbers [math.floor (tens / 10) * 10] 
  
  -- get final digit (eg. thirty four)
  local digits = math.floor (n % 10)

  if digits > 0 then
    result = string.sub (result, 1, -2) .. "-" ..  inverse_numbers [digits]
  end -- if 
  
  return result
  
end -- convert_up_to_999


eg.


Converting: 67707562873705

Result = sixty-seven trillion seven hundred and seven billion five hundred and sixty-two million eight hundred and seventy-three thousand seven hundred and five

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

The dates and times for posts above are shown in Universal Co-ordinated Time (UTC).

To show them in your local time you can join the forum, and then set the 'time correction' field in your profile to the number of hours difference between your location and UTC time.


152,952 views.

This is page 2, subject is 4 pages long:  [Previous page]  1  2 3  4  [Next page]

It is now over 60 days since the last post. This thread is closed.     Refresh page

Go to topic:           Search the forum


[Go to top] top

Information and images on this site are licensed under the Creative Commons Attribution 3.0 Australia License unless stated otherwise.