· 

IEEE754をかじる(1)

IEEE754はコンピュータ内での浮動小数点数の表現方法に関する決まり事です。FPGAでフィルタを実現するにあたって、計算結果の出力時にこの知識が必要です。Intel(ALTERA)のFPGAをQuartusで記述するなら、ALTFP_CONVERTっていうIPがありますが、自力でどうするかを考える。

で、浮動小数点数はコンピュータ内でどのように表現されているのかを調べてみる。

C言語の共用体を使うと簡単です。

#include <stdio.h>
#include <stdint.h>

typedef union{
    uint8_t asByte[4];
    float asFloat;
}uFloat;

int main(int argc,char** argv){
    uFloat ufloat;
    ufloat.asFloat=1.2;
    printf("%g:%02X%02X%02X%02X\n",ufloat.asFloat,ufloat.asByte[3],ufloat.asByte[2],ufloat.asByte[1],ufloat.asByte[0]);
}

出力は

1.2:3F99999A

となる。

こいつから、IEEE754に従って値を取り出すんですが、値の範囲を0以上1未満として、それを一定のビット数で表現するってことを考える。すなわち、0以上1未満の値を0から255で表現するにはどうするか、

#include <stdio.h>
#include <stdint.h>

typedef struct{
    uint32_t frac:23;
    uint32_t expn:8;
    uint32_t sign:1;
}tIEEE754;

typedef union{
    uint8_t asu8[4];
    float asFloat;
    tIEEE754 asIEEE754;
}uFloat;

int main(int argc,char** argv){
    uint32_t nbit=8;
    uint32_t nshift;
    uint32_t ans;
    uFloat ufloat;
    //ufloat.asFloat=0.00390625;//(=1.0/256.0)
    //ufloat.asFloat=0.00390624;
    //ufloat.asFloat=0.000000000000001;
    ufloat.asFloat=0.999999;
    printf("%f:%02X%02X%02X%02X\n",ufloat.asFloat,ufloat.asu8[3],ufloat.asu8[2],ufloat.asu8[1],ufloat.asu8[0]);
    printf("%08X\n",ufloat.asIEEE754.sign);
    printf("%08X\n",ufloat.asIEEE754.expn);
    printf("%08X\n",ufloat.asIEEE754.frac);
    printf("exponent=%d\n",ufloat.asIEEE754.expn);
    if(ufloat.asIEEE754.expn+nbit>=127){
        nshift=ufloat.asIEEE754.expn+nbit-127;
        printf("exponent(updated)=%d\n",nshift);
        ans=(1<<(nshift))+(ufloat.asIEEE754.frac>>(23-nshift));
        printf("value(updated)=%d\n",ans);
        printf("value(real)=%f\n",ufloat.asFloat*(1<<nbit));
    }else{
        ans=0;
        printf("value(updated)=%d\n",ans);
        printf("value(real)=%f\n",ufloat.asFloat*(1<<nbit));
    }
}

いきなり答えを書いてしまった。

ビットフィールドでIEEE754の要素を取り出して、

指数部-127(ここまではIEEE754での決まり)に表現したいビット数を足す。これをnshiftとして、

仮数部は(23-nshift)だけ右シフトする。これで小数点以下がきりすてられる。で、2^(nshift)を足す。

と答えが出る。

このコードの出力は、

 

0.999999:3F7FFFEF

00000000

0000007E

007FFFEF

exponent=126

exponent(updated)=7

value(updated)=255

value(real)=255.999741

 

となっているので、floatでの計算結果value(real)とIEEE754規則に従ってこねくり回した結果value(updated)の整数部が一致していて、期待通り。

if文使ってるいまいちな部分があるけど、入力が1/256より小さいと、nshiftが負の数になってしまうので、そのガード。

で、ごりごりやってみる。

#include <stdio.h>
#include <stdint.h>

typedef struct{
    uint32_t frac:23;
    uint32_t expn:8;
    uint32_t sign:1;
}tIEEE754;

typedef union{
    uint8_t asu8[4];
    float asFloat;
    tIEEE754 asIEEE754;
}uFloat;

int main(int argc,char** argv){
    uint32_t nbit=8;
    uint32_t nshift;
    uint32_t ans;
    uFloat ufloat;
    for(uint32_t i=0;i<100001;i++){
        ufloat.asFloat=(float)i/100000;
        printf("%.12f\t%.12f",ufloat.asFloat,ufloat.asFloat*(1<<nbit));
        if(ufloat.asIEEE754.expn+nbit>=127){
            nshift=ufloat.asIEEE754.expn+nbit-127;
            ans=(1<<(nshift))+(ufloat.asIEEE754.frac>>(23-nshift));
        }else{
            ans=0;
        }
        printf("\t%d\n",ans);
    }
}

結果は長いので省略。あっていることはわかった。

今回のはVHDLで書きなおすための予備検証。ビットフィールドはC言語ならではだけど、VHDLだとむしろより簡単。if文をどうするかとか、結果どのくらいのクロックで実現できるのかとか、(C言語では問題にならなかったけど)ansが2^nbitを超えたときにどうするかが課題。