GoにいればGoに従え(30) math/bitsをTinyGoで使ってみる

Joseph Halfmoon

前回まで標準ライブラリのcontainerを練習してましたが、今回からmathに入ります。最初はmath/bitsです。ここにはMCU(マイクロ・コントローラ・ユニット、間違えてもマーベルなんたらではありませんぞ)使うときに必要になりそうな者どもが控えております。バイナリ者(忍者の一種?)にはお馴染みの操作かと。

※「GoにいればGoに従え」Go関連記事の総Index

go標準ライブラリのmath/bits

標準ライブラリのmathの部には、数学関数だけでなく複素数、多倍長整数に浮動小数など、意欲をそそる(?)ものどもが多数隠れているのでありますが、その中でも一番注目したいのは以下から解説ページに飛べる bits であります。

Standard library > math > bits

呼吸をするようにバイナリを扱う「バイナリ者共」には、慣れ親しんだ小道具といってよいかと。

TinyGoでの利用可能状況を以下のTinyGoドキュメントから調べてみます。

Packages supported by TinyGo

対象の math/bits については、Importable yes とあるので、活用は可能そうです。しかし、Passes tests no らしいので、全面的にOKというわけでもなさそうです。でもま、いつもお世話になっている fmt ですら Passes tests no らしいので、気にせず使ってみることにいたします。自己責任ってやつだな。

さて math/bits には非常に多数の関数が用意されているのですが、それらの多くは、ビット幅指定の「32」ビットなどの数字と組み合わせての組み合わせ爆発なので、機能でまとめてしまえば以下のようになるかと思います。それでも沢山あるけど。

    1. キャリー入力と出力付のAdd
    2. ボロー入力と出力付のSub
    3. 被除数がHi、Loの2要素(除数の倍幅)なDiv、 商と剰余
    4. 上記のDivの剰余のみ求めるRem
    5. 積のビット幅が倍幅になるMul
    6. リーディングゼロのカウント
    7. トレーリングゼロのカウント
    8. 有効ビット幅の取得
    9. ビットが1のカウント
    10. ビット反転
    11. バイト反転
    12. ローテイト(左のみ)

どれも「低レベル」な世界にうごめいているバイナリ者の方々には親しみがあるお道具の数々でしょう。例えば、10番目のビット反転は、MSBファーストとLSBファーストの通信を併用しているようなカオスなシステムでは有効でしょう?11番目のバイト反転は、リトル・エンディアンとビック・エンディアンのマイコンを接続した伝統の?システムで有効でしょう。最近はリトル・エンディアンとMSBファーストに天下統一された感もあるのだけれど、この年寄はどちらも経験ありますぜ。トホホ。

さて今回は、6番目以降のビット操作に近い奴らを練習してみることにします。計算主体の者どもは次回のつもりデス。

今回実験のTinyGoソースコード

例によってさらっと撫でてみるだけの実験用のTinyGoコードが以下に。今回使用しているのは、Goの標準ライブラリばかりなので、TinyGoだけでなく、Goでも実行できそうだけれども(確かめてないデス。)

package main

import (
    "fmt"
    "math/bits"
    "time"
)

func tstRotateLeft8_1(tst uint8) {
    rslt := bits.RotateLeft8(tst, 1)
    fmt.Printf("RotateLeft8, 1bit: 0b%08b -> 0b%08b\n", tst, rslt)
}

func tstReverseBytes(tst uint16) {
    rslt := bits.ReverseBytes16(tst)
    fmt.Printf("ReverseBytes16: 0x%04x -> 0x%04x\n", tst, rslt)
}

func tstReverse8(tst uint8) {
    rslt := bits.Reverse8(tst)
    fmt.Printf("Reverse8: 0b%08b -> 0b%08b\n", tst, rslt)
}

func tstOnesCount8(tst uint8) {
    rslt := bits.OnesCount8(tst)
    fmt.Printf("onesCount: 0b%08b -> %d\n", tst, rslt)
}

func tstLen8(tst uint8) {
    rslt := bits.Len8(tst)
    fmt.Printf("Len8: 0b%08b -> %d\n", tst, rslt)
}

func tstTrailingZeros8(tst uint8) {
    rslt := bits.TrailingZeros8(tst)
    fmt.Printf("TrailingZeros8: 0b%08b -> %d\n", tst, rslt)
}

func tstLeadingZeros8(tst uint8) {
    rslt := bits.LeadingZeros8(tst)
    fmt.Printf("LeadingZeros8: 0b%08b -> %d\n", tst, rslt)
}

func main() {
    var tst uint8 = 0
    for {
        fmt.Println()
        tstRotateLeft8_1(tst)
        tstReverseBytes(0x1234)
        tstReverse8(tst)
        tstOnesCount8(tst)
        tstLen8(tst)
        tstTrailingZeros8(tst)
        tstLeadingZeros8(tst)
        if tst < 255 {
            tst++
        } else {
            tst = 0
        }
        time.Sleep(1000 * time.Millisecond)
    }
}
実験結果

さて、上記のソースをビルドしてUSB接続したターゲット機BBC micro:bit v2 に書き込むコマンドラインは以下です(該当のプロジェクトフォルダで。)

$ tinygo flash --target microbit-v2

シリアルモニタをmicro:bit に向ければ、動作結果を観察することができます。その一部をキャプチャしたものが以下に。

Results

予定通りの動作をキッチリやっておると。目出度い限り。

GoにいればGoに従え(29) container/ringをTinyGoで使ってみる へ戻る

GoにいればGoに従え(31) math/bitsをTinyGoで使う、その2 へ進む