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を超えたときにどうするかが課題。
コメントをお書きください