LoginSignup
5
2

More than 5 years have passed since last update.

[Go言語] 10進数を他の進数に変換する [基数変換]

Posted at

動機

知り合いに10進数を11進数にするプログラム作ってと言われたので、どうせなら11進数だけじゃなく他のものにも対応させたものを作ろうと思いやってみた。

結論

decimalNumに指定された値をbaseNumに指定した進数に変換するソースコードである。
ソースコードの全容は以下

package main

import "fmt"

func main(){
    decimalNum := 1000 // 変換元の10進数
    baseNum := 16 // 何進数に変換するか
    tmpNum := decimalNum // 途中計算を保存する用
    conversionNum := "" // 変換後の値格納用 11進以上はアルファベットのため、文字列で扱う必要がある

    for digitNum := 1;; {
        remainder := tmpNum%baseNum
        if remainder >= 10 {
            conversionNum = string('A' + (remainder-10)) + conversionNum
        } else {
            conversionNum = string('0' + remainder) + conversionNum
        }
        tmpNum = tmpNum/baseNum
        if tmpNum == 0 {
            break
        }
        digitNum *= 10
    }
    fmt.Println(conversionNum)
}

プログラムが出来るまで

1. とりあえず計算式をソースコードへ

まず10進数から別の進数を計算する方法だが、これは意外と単純である。
10進数の値をN進数のNで割り、出てきた商をさらにNで割ることを繰り返す。
そして各徐算で出てきた剰余を並べたものがN進数の値になる。
詳細は省くが、上記の進数の計算方法をコードにしたものは以下になる

func main(){
    decimalNum := 20 // 変換元の10進数
    baseNum := 3 // 何進数に変換するか
    tmpNum := decimalNum // 途中計算を保存する用
    conversionNum := 0 // 変換後の値格納用

    for digitNum := 1;; {
        conversionNum = (tmpNum % baseNum) * digitNum
        tmpNum = tmpNum/baseNum
        if tmpNum == 0 {
            break
        }
        digitNum *= 10
    }
}

しかし、これには大きな問題がある。
剰余が10以上になる11進数以降は計算できない

2. 11進数以上を表現する上での問題・・・

まず、11進数以上の表現にはアルファベットを使用することになる。
そのため変換後のN進数はint型ではなくstring型で扱わなくてはいけない。
しかも、剰余が10の時はA、11の時はB、と剰余の値をアルファベットに変換した上で文字列に結合する必要がある。
方法としては、リストに数字とアルファベットを保存し、対応表を作成するという手段があるが、リストに登録した分までしか基数変換できないため、自分のやりたいどんな進数にでも変換するというのが達成できない。
せめて文字コードを計算に使用することができれば…と調べたところ「rune」というものがあるらしい

3. 「rune」による文字コードの操作

runeとはint32型の実態をもち、さらに文字を簡単に文字コードに変換してくれるらしい。
また、int型のため足し算ができる。
その上、計算結果は「string()」によって文字コードの対応した文字に戻せるとのこと。
実際に検証してみたところ

// シングルコーテーションで囲むことによってrune()に文字を渡すのと同じ意味になる
fmt.Println('A')
// 出力結果... 65

fmt.Println('A'+1)
// 出力結果... 66

fmt.Println(string('A'+1))
// 出力結果... "B"

ちゃんと変換されている。(文字コードを見る限りASCIIコードだろうか?)

ここまで分かればあとはもう10以上の剰余が出たら'A'の文字コードに剰余-10の値を足してやるようにコードを書き直せばいい。

完成品

結論でも記述したが、完成したものが以下である。

package main

import "fmt"

func main(){
    decimalNum := 1000 // 変換元の10進数
    baseNum := 16 // 何進数に変換するか
    tmpNum := decimalNum // 途中計算を保存する用
    conversionNum := "" // 変換後の値格納用 11進以上はアルファベットのため、文字列で扱う必要がある

    for digitNum := 1;; {
        remainder := tmpNum%baseNum
        if remainder >= 10 {
            conversionNum = string('A' + (remainder-10)) + conversionNum
        } else {
            conversionNum = string('0' + remainder) + conversionNum
        }
        tmpNum = tmpNum/baseNum
        if tmpNum == 0 {
            break
        }
        digitNum *= 10
    }
    fmt.Println(conversionNum)
}

ひとまず36進数までなら確実に表示はできるし、37以上でも記号を使用して表現できるのが確認できたので満足。
100とかはわからん

ここまでやったけど実は・・・

下記コードで簡単に2進数から36進数まで変換できる。

package main

import (
    "fmt"
    "strconv"
)

func main() {
    for i := 2; i <= 36; i++ {
        fmt.Println(strconv.FormatInt(10000000, i))
    }
}

ソース引用元: http://y0m0r.hateblo.jp/entry/20131016/1381936219

strconvに存在していたらしい、知らなかった。
記事を書いてる時に見つけてしまってやめるかどうか悩んだが、せっかく書いたので生き恥を晒すことにした。

GO歴数ヶ月なので色々拙いけど、ゆるちて

5
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
2