chibiccー6800 の浮動小数点演算の効率化作業をしているんだけど、その過程でいくつかの知見を得た。たぶん、過去の文献や資料にある話だと思うのだけど、メモしておく。
ここでの対象は MC6800用のCコンパイラ chibicc用の浮動小数点ライブラリ。32bit floatを扱える。丸めは最近偶数丸めのみ。
- zu2/chibicc-6800-v1: C Compiler for MC6800 (fork from chibicc)
- chibicc-6800-v1/libm/__float.s at main · zu2/chibicc-6800-v1
IEEE754浮動小数点の実装
普段使ってるパソコンでは、浮動小数点演算はCPUがハードウェアで実行してくれるし、識者の作ったライブラリも揃っている。普通に使っている分には精度も速度も心配ない。
MC6800では そうはいかない。浮動小数点ハードウェアはない。それどころか整数の乗除算すらない。加減算は8bit単位だ。実に面倒臭い。
それでも、chibiccの移植を初めて2週間後には それっぽい浮動小数点ライブラリができていた。
このバージョンは 加減乗除と比較が動くだけのシロモノで、丸めも非正規化数もない。その頃の記事は以下にある。
- chibicc compiler を6800向けに改造する (6) IEEE754 float (1) – ず@沖縄
- chibicc compiler を6800向けに改造する (8) IEEE754 float (2) – ず@沖縄
- chibicc compiler を6800向けに改造する (9) IEEE754 float の比較(3) – ず@沖縄
実装には以下の書籍が参考になった。
IEEE754浮動小数点演算の丸め
さて、上記書籍にも書かれているが、丸めのためには仮数24bitを超える部分の情報が必要になる。

25,26bit目をGuard/Round bitと呼び、27bit目以降で1が立つビットがあればstick bitを立てる。この3bitと仮数の最下位ビット(L)を使って丸めを行う。
最近接偶数丸めだと round_up = G && (R || S || L); になる。
| G | R | S | 意味 | L=0 | L=1 |
|---|---|---|---|---|---|
| 0 | 0 | 0 | 正確 | そのまま | そのまま |
| 0 | 0 | 1 | 1/4 ulp 未満 | そのまま | そのまま |
| 0 | 1 | 0 | 1/4 ulp | そのまま | そのまま |
| 0 | 1 | 1 | 1/4~1/2 ulp | そのまま | そのまま |
| 1 | 0 | 0 | ちょうど 1/2 ulp(tie) | そのまま | +1 |
| 1 | 0 | 1 | 1/2~3/4 ulp | +1 | +1 |
| 1 | 1 | 0 | 3/4 ulp | +1 | +1 |
| 1 | 1 | 1 | 3/4~1 ulp | +1 | +1 |
IEEE754浮動小数点除算の丸め
除算の場合は 少し計算を効率化できる部分がある。
除算は1回のループで1bitずつ求められるので、26bit計算して止める。このときの剰余が Sticky bit になる。剰余があれば S=1、なければ S=0 だ。
(剰余がStickyであることに気がつくまで5ヶ月かかっている)
最近、AIと議論してわかったのだが、実は除算の場合だけはもっと効率化できる。以下は2進数での除算が前提。
(非正規化数が絡むとややこしくなるので、正規化数の範囲で考える)
除算の結果は、循環する無限小数になるか割り切れるかのどちらかである。
割り切れる場合、被除数/除数を約分した分母は2の
2の冪乗であるということは、これは割算ではなく指数の調整だけで済む。ということは、除算後の仮数は24bit以下になる。したがって、S=0であればG=0,R=0である。
現れるパターンは 000 / 001 / 011 / 101 / 111 だけ。 G=0だと桁上げなしだし、G=1なら(Lに関わらず)桁上げが発生する。R/Sは見なくても良い。
使うのはGだけだが、商の最上位ビットが0になる場合は正規化で1bit喰われるので、26bit求めておく必要はある。
平方根も同様
仮数24bitを整数に見立てると、平方根の結果は整数になるか無理数である。整数ならS=0であり除算と同じ議論が成り立つ。
無理数の場合は1/2(有理数)になることはないので、G=1であれば切り上げて良い。
平方根では、丸めの結果で桁上がりして最上位ビットを超すことはないので、桁上げ後の補正もいらない。

コメント