たまりば

  パソコン・インターネット パソコン・インターネット  三鷹市 三鷹市

PICで1バイト浮動小数点数
2012年05月14日 00:08

ふとPICで浮動小数点数を扱いたくなったのでアルゴリズムを作ってみた。

もっとも、浮動小数点数というと語弊があるかもしれない。そんな大層なものではない。
ちょっとテーブルを作りたかったのだが、そのまま作ると1個当たり2バイトの容量になってしまうのでそれを1バイトに圧縮したというだけである。
なので、これで演算をすることは考えていない。また、PIC上でエンコードすることも考えていない。

目標はこんな感じである。
・1バイトの浮動小数点数。
・表す値は2バイト。
・デコードをなるべく単純に。エンコードのことは知らん。

これで作ってみたら以下のようになった。
まず仕様はこんな感じ。
仮数部 : 4bit (ケチ表現で実質5bit)
指数部 : 4bit
符号 : なし
最小値 : 2 0 (ただし1が飛んでいる) (5/15訂正)
最大値 : 63488 (0xF800)

ただしエンコードを軽視したために指数部のビットパターンが少々複雑になった。ビットパターンは次のとおりである。
・上4bitが仮数部 (ケチ表現なので5bitの値の下4bit)
・下4bitは上から順に、指数部の3bit目、1bit目、0bit目、2bit目
なお、普通の順にしてもデコードの途中で1回シフトすればいいだけなのでこんな変なことをすることもなかったかもしれない。まあいいや。

他にもいくつか問題がある。
両端の値が表せない。0, 1, 65535は表したいところである。←0は表せました。
指数部を普通の順にしたとして、指数部と仮数部の順が普通と逆なので、数値が順に並ばない。
仮数部・指数部4bitづつでは普通20bitの範囲が表せるがそれを16bitに制限したため、無駄が多い。
 256個の値のうち有効なものは206207個しかない。

直そうと思えば直せるものもあると思うが、今回の目標としてデコードを単純にするというのがあるので、気にしないことにする。

デコード用プログラムは以下。33ワード。最大35サイクル。
;使用するレジスタ
;ansh: デコードされた値(上位)
;ansl: 〃(下位)
;exp: 指数
;exp10: 指数の0,1bit目

;使い方
;Wに浮動小数点数を入れてCALL
;<ansh:ansl>にデコードされた値が入る

decodefloat
;各種値をセット
 clrf ansh
 movwf ansl
 andlw 0x0F
 movwf exp
 andlw 0x06
 movwf exp10
 movlw 0xF0
 andwf ansl,F
;exp: 0000nnnn
;exp10: 00000nn0
;ansh:ansl: 00000000 nnnn0000

;指数がx0xxならば4bitずらす
;同時に、仮数の5bit目を立てる
 btfsc exp,0 ;指数<2>
 goto shift4end ;if x0xx, then do
 swapf ansl,F
 bsf ansl,4
 btfsc exp,0
shift4end
 bsf ansh,0 ;if x1xx

;指数 ansh   ansl
;x1xx: 00000001 nnnn0000
;x0xx: 00000000 0001nnnn

;指数が1xxxなら8bitずらす
;同時に、指数が11xxなら仮数の5bit目としてtempフラグを立てる
;tempフラグは空いているexp<7>を流用
 btfss exp,3
 goto shift8end ;if 1xxx, then do
 movf ansl,W
 movwf ansh
 clrf ansl
 btfsc exp,0 ;指数<2>
 bsf exp,7 ;if 11xx, then do
shift8end

;11xx: nnnn0000 00000000, temp=1
;10xx: 0001nnnn 00000000, temp=0
;01xx: 00000001 nnnn0000, temp=0
;00xx: 00000000 0001nnnn, temp=0

;指数<1:0>に応じて右シフト
 movlw low(jumptarget) ;ジャンプ先アドレスをWにセット
 addwf exp10,W ;。
 btfsc exp,7 ;tempフラグをCにセット
 bsf STATUS,C ;。
 movwf PCL ;exp<2:1>の値に応じてジャンプ
;ここからページ境界不可→
jumptarget
 rrf ansh,F ;if 0 then 3回シフト
 rrf ansl,F
 rrf ansh,F ;if 1 then 2回シフト
 rrf ansl,F
 rrf ansh,F ;if 2 then 1回シフト
 rrf ansl,F
 return ;if 3 then シフトしない
;→ここまで

全ての有効な値を順に並べると以下のとおり。
見て分かるように順序が分かりづらい。
ここに書かれていない値を入れた場合どうなるかは試していないが、他と全く同じかほぼ同じ値になるはずである。暴走は多分しない。
 DT 0x0F, 0x00, 0x80, 0x02, 0x42, 0x82, 0xC2, 0x04 (5/15 0x0F(0に相当)を追加)
 DT 0x24, 0x44, 0x64, 0x84, 0xA4, 0xC4, 0xE4, 0x06
 DT 0x16, 0x26, 0x36, 0x46, 0x56, 0x66, 0x76, 0x86
 DT 0x96, 0xA6, 0xB6, 0xC6, 0xD6, 0xE6, 0xF6, 0x01
 DT 0x11, 0x21, 0x31, 0x41, 0x51, 0x61, 0x71, 0x81
 DT 0x91, 0xA1, 0xB1, 0xC1, 0xD1, 0xE1, 0xF1, 0x03
 DT 0x13, 0x23, 0x33, 0x43, 0x53, 0x63, 0x73, 0x83
 DT 0x93, 0xA3, 0xB3, 0xC3, 0xD3, 0xE3, 0xF3, 0x05
 DT 0x15, 0x25, 0x35, 0x45, 0x55, 0x65, 0x75, 0x85
 DT 0x95, 0xA5, 0xB5, 0xC5, 0xD5, 0xE5, 0xF5, 0x07
 DT 0x17, 0x27, 0x37, 0x47, 0x57, 0x67, 0x77, 0x87
 DT 0x97, 0xA7, 0xB7, 0xC7, 0xD7, 0xE7, 0xF7, 0x08
 DT 0x18, 0x28, 0x38, 0x48, 0x58, 0x68, 0x78, 0x88
 DT 0x98, 0xA8, 0xB8, 0xC8, 0xD8, 0xE8, 0xF8, 0x0A
 DT 0x1A, 0x2A, 0x3A, 0x4A, 0x5A, 0x6A, 0x7A, 0x8A
 DT 0x9A, 0xAA, 0xBA, 0xCA, 0xDA, 0xEA, 0xFA, 0x0C
 DT 0x1C, 0x2C, 0x3C, 0x4C, 0x5C, 0x6C, 0x7C, 0x8C
 DT 0x9C, 0xAC, 0xBC, 0xCC, 0xDC, 0xEC, 0xFC, 0x0E
 DT 0x1E, 0x2E, 0x3E, 0x4E, 0x5E, 0x6E, 0x7E, 0x8E
 DT 0x9E, 0xAE, 0xBE, 0xCE, 0xDE, 0xEE, 0xFE, 0x09
 DT 0x19, 0x29, 0x39, 0x49, 0x59, 0x69, 0x79, 0x89
 DT 0x99, 0xA9, 0xB9, 0xC9, 0xD9, 0xE9, 0xF9, 0x0B
 DT 0x1B, 0x2B, 0x3B, 0x4B, 0x5B, 0x6B, 0x7B, 0x8B
 DT 0x9B, 0xAB, 0xBB, 0xCB, 0xDB, 0xEB, 0xFB, 0x0D
 DT 0x1D, 0x2D, 0x3D, 0x4D, 0x5D, 0x6D, 0x7D, 0x8D
 DT 0x9D, 0xAD, 0xBD, 0xCD, 0xDD, 0xED, 0xFD

余談。
このプログラムを作るにあたって1ヶ所はまった所がある。
最後の0~3回シフトする所なのだが、ここで有効なbitが最上位の4bitだった場合にケチ表現の1bitを入れるのを忘れていた。
キャリーに入れればいいのはわりとすぐ気づいたのだが、ここで問題が起こった。
最初のコードはこんな感じだった。
 movf exp10,W ;指数<1:0>をワーキングレジスタにセット
 addwf PCL ;プログラムカウンタに加算
 rrf ansh,F
 rrf ansl,F
 rrf ansh,F
 rrf ansl,F
 rrf ansh,F
 rrf ansl,F
 return
プログラムカウンタに値を加算するのはPIC特有のイディオムで、PICには変数でジャンプする命令が無い代わりにプログラムカウンタに対して演算する設計になっている。
なので今回も相対ジャンプのために何も考えずに使っていたのだが、これではプログラムカウンタに加算するときにキャリーが破壊されてしまうので、今回のようにキャリーを使いたい時に困る。
破壊されるなら別の場所に一時保存しておけばいいかと思ったが、相対ジャンプの直後に使うことになるので不可能である。
これはどうしようもないかなと思ったときに思いついたのが上記のコードである。ジャンプ先のアドレスを先に計算しておけばよかったのだ。

PICを使っているとどうもこういうことが多い。欲しい機能が無くてアルゴリズムが実装できないかと思うと、よく考えてみると他の機能で代替できる。
PICのアーキテクチャはよく考えられているのだなあと思う。

-----
5/15 訂正
0は表せないと思っていたが表せたので各所を訂正。


  • Post time : 2012年05月14日 00:08│Comments(0)
    URL欄を実験的に消してる間に廃止されてしまいました。まあいいか。
     
    <ご注意>
    書き込まれた内容は公開され、ブログの持ち主だけが削除できます。
    削除
    PICで1バイト浮動小数点数
      コメント(0)