[C#] 自定義(自訂)進位制

  • 8744
  • 0
  • 2018-08-31

[C#] 自定義(自訂)進位制

久違的文章...

一般進位制是以1~10(十進位制)或是1~A~F(十六進位制)以及1~A~Z(三十六進位制)較常見,但最近(事實上是昨天)因為工作需要,必須採用三十六進位制,但需濾掉易與數字產生混淆的 I, O, U 等三個字元,因此得自己弄一個進制。不過昨天只寫了單向的機制,反向機制因為沒需求所以就沒寫,有需要的請自行反向思考。


動手前先釐清一下進位制的觀念。在十進位制轉成十六進位制的例子中,是將原本的十進位制數字除以16的各次方,而取其商為十六進位制的數。例如我想要將十進位制的100轉為十六進位制的數字,首先判斷小於100的最大16次方數是哪一個,因為clip_image002[4],所以最大的次方數是1,也因此我們知道100轉成十六進位制之後只會是兩位數。接著就直接把商數求出來:

clip_image002[6],商數為6,所以第一位數是6;餘數為4,所以下一步的被除數要換成4。

clip_image002[8],商數為4,所以第二位數是4;餘數為0(等於次方數為0,因為所有整數被1除都會整除,所以餘數一定為0),所以轉換結束。

 以上的步驟就能將十進位制的100轉成十六進位制的64。

 

好的,接著就來將以上的邏輯轉化成程式碼,並加一點變化(自定義進位制)。由於我必須濾掉Base36的其中3個字元,因此我先將濾掉之後的33個字元存在陣列裡面備用(因為我打算做成靜態方法以供叫用,所以這個陣列member也定義成靜態的。另外也可以朝著延伸方法去做,事實上我也prefer那樣的做法,不過這邊只是做個範例,因此對於延伸方法就不多加贅述):

/// <summary>
/// 自定義進位制所對應的陣列
/// </summary>
public static readonly string[] mappingArray = new string[] {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
                                                            "A", "B", "C", "D", "E", "F", "G", "H", "J", "K",
                                                            "L", "M", "N", "P", "Q", "R", "S", "T", "V", "W",
                                                            "X", "Y", "Z"};

接著定義靜態方法(因為我打算轉成字串輸出,所以方法名稱多了String):

/// <summary>
/// 將十進位數字轉成自定義進位制字串
/// </summary>
/// <param name="originalNumber">原始十進位數字</param>
/// <param name="digitCount">1.字串位數</param>
/// <returns>自定義進位制字串</returns>
public static string ToBase33String(int originalNumber, int digitCount)
{
    string outputNumberString = "";
    while (digitCount > 0)
    {
        //2.第一位數是(digitCount-1)次方, 所以先減1
        digitCount--;
        //餘數
        int remainder = 0;
        //商數
        int quotient = Math.DivRem(originalNumber, (int)Math.Pow((double)33, (double)digitCount), out remainder);
        //3.溢位檢查
        if (quotient >= 33) throw new Exception("Stack Overflow!! Please input a smaller OriginalNumber or a bigger DigiConut");
        //4.字串組裝
        outputNumberString += Program.mappingArray[quotient];
        //餘數轉為被除數
        originalNumber = remainder;
    }
    return outputNumberString;
}

很明顯的,程式碼比前面的文字描述來得易懂多了。雖然基本上是依照前述來做除法已取出商數及餘數並做迴圈,但這邊有幾個需要特別注意的地方:
1. digitCount是指我想要取得的字串長度,例如字串000A3的長度是5,並且因為不足的位數需要用0補滿,所以我也不需知道小於原數字的最大33次方數為何
2. 有如十六進位制,假如字串長度是3,則其百位數是除以16的2次方,十位數是除以16的1次方,個位數是除以16的0次方,也就是說第一個次方數是字串的長度減1,因此在while迴圈裡面會先減1
3. 溢位的部分只要判斷是不是大於或等於33,因為商數如果達到33的話是必須進位的,所以我用這個來判斷是否溢位
4. 這邊因為我是要濾掉IOU三個字元,所以無法直接使用char跟int互轉的方式,也因此前面需要定義靜態陣列來給後面這邊做mapping

以上就是昨天弄的三十三進位制,希望多少幫到跟我有一樣需求的人,特別是將十進位制的流水碼轉換成自定義進位制流水碼的case~