2018年5月27日日曜日

独自float16

 マイコンで生成したint64_tの配列をPCに転送したいので、独自の浮動小数点を考えてみた。

 似たような規格として、既存の半精度浮動小数点数があるが、マイコンでのサポートがない(多分)、表現できる範囲が狭い、といった問題がある。
 前者に関しては、それっぽい値に変換するライブラリをかけば済む話だが、後者が問題で、IEEE 754の半精度浮動小数点数は10進でたかだか4.8桁程度しか表現できない。int64_tは10進で18.9桁ほどあるので、圧倒的に足りない。
 また、一般的な浮動小数点は1未満かつ-1より大きい値を表現できるが、あくまで整数の中間表現がほしいだけなので、1未満の分解能は必要ない。ということで、符号1bit、指数6bit、仮数9bit、の16bit浮動小数点を考えた次第。

 これにより、8バイトの64bit整数を2バイトで表現できるようになったので、4分の1のメモリサイズで済む。

 値が小さい時(<1024)は少し特殊な処理をしている。
 たぶん最小値(-2^63)を与えるとバグる。
 今回はそんな端っこまで使う予定はなかったので省いた。というかint32でも足りるんじゃないか、程度の範囲しか使っていないんだよなぁ。浮動仮数幅浮動小数点、なんてのもありかもしれない。

using original_float_16 = uint16_t;

original_float_16 convert_to_BIN(int64_t value)
{
    const bool is_negative(value < 0);

    if (is_negative)
    {
        value = -value;
    }

    uint16_t bit_data(0);

    if (value < 1024)
    {
        bit_data = value;
    }
    else
    {
        uint_fast8_t i(53);
        while (i > 0) // exit with break
        {
            if (value & ((uint64_t)1 << (i + 9)))
            {
                bit_data = ((i + 1) << 9) | ((value >> i) & 0x01FF);
                break;
            }
            i--;
        }
    }

    if (is_negative)
    {
        bit_data |= 0x8000;
    }

    return (bit_data);
}

int64_t convert_from_BIN(original_float_16 BIN)
{
    const uint_fast8_t sign((BIN >> 15) & 0x01);
    const uint_fast8_t exponent((BIN >> 9) & 0x3F);
    const uint_fast16_t fraction(BIN & 0x01FF);

    const int64_t result(exponent
                             ? (uint64_t)(fraction | 0x0200) << (exponent - 1)
                             : fraction);

    return (!sign ? result : -result);
}

 もうちょっとうまくかけそうな気もするけど、とりあえず動いてるので。

 fp→intは各ビットを取り出してビットシフトするだけなので比較的簡単。
 int→fpは最上位ビットの位置を探す必要があるのでループしてる。最上位ビットの位置を1クロックで返す命令とか無いものか。

0 件のコメント:

コメントを投稿