How to programmatically get the numbering system Scale Name String from its power without using predefined list of Scale Names

Viewed 137

TL;DR, Examples:

Given 0  output results is : ''                (because 1000^0 = zero)
      1  output results is : 'thousand'        (because 1000^1 = thousand)
      2  output results is : 'million'         (because 1000^2 = million)
      3  output results is : 'billion'         (because 1000^3 = billion)
      4  output results is : 'trillion'        (because 1000^4 = trillion)
      .....
     20  output results is : 'novendecillion'  (because 1000^20 = novendecillion)
    351  output results is : 'quinquagintatrecentillion'
    454  output results is : 'tresquinquagintaquadringentillion'
   1000  output results is : 'novenonagintanongentillion'

For Details:

When converting numbers to English words, the need arises to state the Scale Name (i.e. thousands, millions, trillions, etc.). This can be as large as necessary depending on the number generated. In such cases, the Scale Names are typically placed in an array.

If the output needs to use very large numbers then the Scale Names need to extend to large scale names, sometimes above 10^200, then this becomes a problem as the array becomes very large and is unreasonable to hardcode such a large number of strings in an array, and may result in typos and errors.

Given the power of a scale name, how to return the scale name string programmatically?

More details and information on the Names of large numbers can be found here on Wikipedia (Names of large numbers). For simplicity, use the Short Scale Numeral System used in (USA, UK, Canada, i.e. System using Billion instead of Milliard).

The Scale Names are powers to the base number 1000.

 0 means 1000^0  = ""
 1 means 1000^1  = thousand
 2 means 1000^2  = million
 3 means 1000^3  = billion
 4 means 1000^4  = trillion
11 means 1000^11 = decillion

Scales Names up to the power of 1000 are sufficient as this means a number with 3000 zeros on the right hand of the number.

Output is needed in the Short Scale Numbering System (English Language) format.

Other formats such as the Long Scale Numbering System (European/French System) or in other languages French, Arabic, Persian, etc. are appreciated.


My answer in the Short Scale Numbering System (English Language) follows as an answer to this question as recommended by StackOverflow here: Can I answer my own question?

1 Answers

This JavaScript code generates the full name of the numeral scales for large numbers under the "Short Scale Numeral System" using the Conway-Guy system for forming number prefixes.

Information on the Conway-Guy system can be found at the end of this Wikipedia article.

The naming procedure for large numbers is based on taking the number power of the number and concatenating the Latin roots for its units, tens, and hundreds place, together with the suffix "llion".

Note the sequence and order of the concatenation is from lowest to largest (this opposite to how we pronounce and write numbers such as 123 [one hundred twenty-three]).

This way, numbers up to 10^3000 may be named easily.

Prefixes are constructed based on a system described by John Horton Conway and Richard K. Guy using the Latin roots.

The Conway-Guy System for forming prefixes is included in the following table:

enter image description here

The Scale Name can be generated by concatenating the Unit Name, Tens Name, Hundreds Name, and adding "llion" to the end of the concatenated string as illustrated below:

enter image description here

However, concatenation is not that straight forwards because four (4) of the Unit Names (tre (3), se (6), septe (7), and nove (9)) cannot be concatenated directly "as is" and require modification depending on the Tens Name or Hundred Name that follows them.

For example: unit name "tri (3)" need to be changed to "tres" if it is to be concatenated with (say) 20 (viginti) or with 300 (tricinti).

Similarly, "se" need to be changed to "ses" or "sex", "septe" and "nove" also need to be changed to "septem" and "novem" or "septen" and "noven" respectively depending on the tens or hundreds name.

Therefore, for each Tens Name and Hundreds Name, there is a corresponding Unit Name that can precede it for the purpose of concatenation (this is the purpose of the so ugly array).

The illustrative tables below list the changes needed for each Unit Name depending on the Tens Name and the Hundreds Name.

enter image description here

enter image description here

One additional requirement to be considered when concatenating the various names is that Unit Names ending in "a" need to be changed to end with "i" when they are to be concatenated with the word "llion". Names ending with "a" only exist in the Tens Names!

For example:

The number 10^31 ==> "triginta" becomes "triginti" + "llion" ==> trigintillion

The number 10^81 ==> "octoginta" becomes "octoginti" + "llion" ==> octogintintillion

The handling of this situation is catered for in the "Tens" array; the last element made as Boolean True being a marker.

Additional Examples are shown in the image below:

enter image description here

The input to the function is a number which is the power of the base 1000.

  • 1 means 1000^1 = 1,000
  • 2 means 1000^2 = 1,000,000 (million)
  • 3 means 1000^3 = 1,000,000,000 (billion)

The code function is made to generate the Scale Names for the Short Scale Numeral System used in (USA, UK, Canada, i.e. System using Billion instead of Milliard). You can read about the differences between the Short and Long Scale Numeral Systems here.

The code can be modified easily to generate the strings for the Long Scale System using the same arrays. In this case, the maximum power is 10^6000 (i.e. 1 with 6000 zeros).

Test code is included that tests various numbers.

/************************************************************************
* @Function    : getScaleName()
* @Purpose     : Get the full name of the Short Scale Numeral System
*                Using the Conway-Guy system for forming number prefixes
*                Handles power from 0 to 1000
*                The largest scale name is, therefore, the number with
*                3,000 zeros (Novenonagintanongentillion)
*
* @Version     : 1.00
* @Author      : Mohsen Alyafei
* @Date        : 11 Mar 2022
* @Param       : {number} the power numeral of the base 1000
*                e.g.  1 means 1000^1 = 1,000 (thousand)
*                e.g.  2 means 1000^2 = 1,000,000 (million)
*                e.g.  3 means 1000^3 = 1,000,000,000 (billion)
*                e.g.  4 means 1000^4 = 1,000,000,000,000 (trillion)
*                e.g. 11 means 1000^11 = decillion
*
* @Returns     : {string} The name of the large number
* @Reference   : https://en.wikipedia.org/wiki/Names_of_large_numbers
*************************************************************************/


//===================================================================
function getScaleName(power=0) {
if (power<11) return power<2?["","thousand"][power]:["m","b","tr","quadr","quint","sext","sept","oct","non"][power-2]+"illion";
power-=1;
let tensList = [
[              ,[,,,,,,,,,,0]],
["deci"        ,[,,,,,,,"n",,"n",0]],       // 10
["viginti"     ,[,,,"s",,,"s","m",,"m",0]], // 20
["triginta"    ,[,,,"s",,,"s","n",,"n",1]], // 30
["quadraginta" ,[,,,"s",,,"s","n",,"n",1]], // 40
["quinquaginta",[,,,"s",,,"s","n",,"n",1]], // 50
["sexaginta"   ,[,,,,,,,"n",,"n",1]],       // 60
["septuaginta" ,[,,,,,,,"n",,"n",1]],       // 70
["octoginta"   ,[,,,,,,"x","m",,"m",1]],    // 80
["nonaginta"   ,[,,,,,,,,,,1]]              // 90
],
hundredsList = [
[              ,[,,,,,,,,,]],
["centi"       ,[,,,,,,"x","n",,"n"]],    // 100
["ducenti"     ,[,,,,,,,"n",,"n"]],       // 200
["trecenti"    ,[,,,"s",,,"s","n",,"n"]], // 300
["quadringenti",[,,,"s",,,"s","n",,"n"]], // 400
["quingenti"   ,[,,,"s",,,"s","n",,"n"]], // 500
["sescenti"    ,[,,,,,,,"n",,"n"]],       // 600
["septingenti" ,[,,,,,,,"n",,"n"]],       // 700
["octingenti"  ,[,,,,,,"x","m",,"m"]],    // 800
["nongenti"    ,[,,,,,,,,,]]              // 900
],
hund     = ~~(power / 100),               // Hundred Digit
ten      = ~~(power % 100 / 10),          // ten Digit
unit     = power % 10 % 10,               // unit Digit
unitName = ["","un","duo","tre","quattuor","quin","se","septe","octo","nove"][unit], // Get unit Name from Array
tenName  = tensList[ten][0],              // Get Tens Name from Array
hundName = hundredsList[hund][0];         // Get Hundreds Name from Array
tenName??=""; hundName??="";              // make it an empty string if undefined

// convert ten names ending with "a" to "i" if it was proceeding the "llion" word
if (!hund && tensList[ten][1][10]) tenName = tenName.slice(0,-1)+"i";
// Pickup and add the correct suffix to the unit Name (s,x,n, or m)
if (ten) tenName           =  (tensList[ten][1][unit]     ??="")+tenName;
else if (hund && !ten) hundName =  (hundredsList[hund][1][unit]??="")+hundName;
return unitName + tenName + hundName + "llion";  // Create name
}
//===================================================================














//=========================================
//             Test Codes
//=========================================

var r=0; // test tracker
r |= test(0,"");
r |= test(1,"thousand");
r |= test(2,"million");
r |= test(3,"billion");
r |= test(4,"trillion");
r |= test(5,"quadrillion");
r |= test(6,"quintillion");
r |= test(7,"sextillion");
r |= test(8,"septillion");
r |= test(9,"octillion");
r |= test(10,"nonillion");
r |= test(11,"decillion");
r |= test(12,"undecillion");
r |= test(13,"duodecillion");
r |= test(14,"tredecillion");
r |= test(15,"quattuordecillion");
r |= test(16,"quindecillion");
r |= test(17,"sedecillion");
r |= test(18,"septendecillion");
r |= test(19,"octodecillion");
r |= test(20,"novendecillion");
r |= test(21,"vigintillion");
r |= test(22,"unvigintillion");
r |= test(23,"duovigintillion");
r |= test(24,"tresvigintillion");
r |= test(25,"quattuorvigintillion");
r |= test(26,"quinvigintillion");
r |= test(27,"sesvigintillion");
r |= test(28,"septemvigintillion");
r |= test(29,"octovigintillion");
r |= test(30,"novemvigintillion");
r |= test(31,"trigintillion");
r |= test(32,"untrigintillion");
r |= test(33,"duotrigintillion");
r |= test(34,"trestrigintillion");
r |= test(35,"quattuortrigintillion");
r |= test(36,"quintrigintillion");
r |= test(37,"sestrigintillion");
r |= test(38,"septentrigintillion");
r |= test(39,"octotrigintillion");
r |= test(40,"noventrigintillion");
r |= test(41,"quadragintillion");
r |= test(51,"quinquagintillion");
r |= test(61,"sexagintillion");
r |= test(71,"septuagintillion");
r |= test(81,"octogintillion");
r |= test(91,"nonagintillion");
r |= test(101,"centillion");
r |= test(102,"uncentillion");
r |= test(111,"decicentillion");
r |= test(112,"undecicentillion");
r |= test(121,"viginticentillion");
r |= test(122,"unviginticentillion");
r |= test(131,"trigintacentillion");
r |= test(141,"quadragintacentillion");
r |= test(151,"quinquagintacentillion");
r |= test(161,"sexagintacentillion");
r |= test(171,"septuagintacentillion");
r |= test(181,"octogintacentillion");
r |= test(191,"nonagintacentillion");
r |= test(201,"ducentillion");
r |= test(251,"quinquagintaducentillion");
r |= test(301,"trecentillion");
r |= test(351,"quinquagintatrecentillion");
r |= test(378,"septenseptuagintatrecentillion");
r |= test(401,"quadringentillion");
r |= test(451,"quinquagintaquadringentillion");
r |= test(454,"tresquinquagintaquadringentillion");
r |= test(501,"quingentillion");
r |= test(601,"sescentillion");
r |= test(701,"septingentillion");
r |= test(801,"octingentillion");
r |= test(901,"nongentillion");
r |= test(999,"octononagintanongentillion");
r |= test(1000,"novenonagintanongentillion");

if (r==0) console.log("All Test Units Passed.");


//================
function test(n,should) {
var result = getScaleName(n);
if (result !== should) {
    console.log(`${n} Output   : ${result}`);
    console.log(`${n} Should be: ${should}`);
    return 1;
}
}

Related