なまけもの日記

人生のメモ帳

符号付き固定小数点演算の形式変換処理

10進数の実数と32bitの符号付き固定小数点演算の形式変換処理

研究の都合上,FPGA上で符号付き固定小数点演算を取り扱うことになりました.固定小数点演算を用いた演算器は整数演算と基本的に同じなので特に困ることはありませんが,回路上では全て0と1のbitで処理されるため,固定小数点演算や浮動小数点演算の区別は当然なく,入力値や出力値は01のbitとなります.そして,固定小数点演算ではbit数などのフォーマットが任意のため,入力値を生成する際には自分でbitの形式に変換する必要があり,さらに単純にテストベンチ上での$display等で出力値を表示させることができません.何とかするために調べましたが,浮動小数点演算形式はIEEE754規格に準拠しているため変換ツールなどがいろいろありますが,固定小数点演算形式は任意のため変換ツールがなく,見つけることができませんでした.「無いなら自分で作ればいいか.」ということで,ソースコードの綺麗さ等は何も考えずにとりあえず作成.固定小数点演算のbit数などは浮動小数点演算と比較するために暫定的に32bitと仮定し,ついでにFPGA開発時に使用している複数の言語で実装してみました.全て自分用なので利用する上で不具合等がありますが,メモとして残しておきます.

変換の例

固定小数点演算形式は符号付き32bit(符号1bit,整数20bit,小数11bit)

  • 10進数表記(実数)→2進数表記(固定小数点)
-2.74929489937376
↓
111111111111111111101.01000000010(ピリオド.は確認用)
  • 2進数表記(固定小数点)→10進数表記変換(実数)
111111111111111111101.01000000010
↓
-2.7490234375(量子化誤差が発生)

実装言語

エクセルVBAVB.net

エクセル上でデバッグ用のパラメータ生成や簡易評価に利用.エクセルの関数として利用する形式で,入力値のセルは数値ではなく文字列.エクセルには BIN2DEC や DEC2BIN などの関数が用意されているが,用意されている関数は固定小数点演算や32bit長には対応していない.

  • 2進数表記→10進数表記変換
'==================================================
' 固定小数点(2進数,符号付き32bit)を実数(10進数)へ変換
' 符号部1bit,整数部20bit,小数部11bit(bit幅は調整可能)
' Input  : 11111111111111111110101000000010(※文字列)
' Outpot : -2.7490234375
'==================================================
Public Function FixedPoint2DEC(dataa As String) As Double

Dim IntBit As Integer  ' 整数部のbit幅
Dim DecBit As Integer  ' 小数部のbit幅
Dim SignBit As Long    ' 符号部のbit
Dim BitLength As Long  ' bit幅
IntBit = 20
DecBit = 11
BitLength = IntBit + DecBit + 1

Dim IntNum As Double '整数部
Dim DicNum As Double '小数部
Dim Result As Double '実数
Dim data(32) As Integer
Dim i As Integer
Dim temp As String


Dim temp_data As String
temp_data = dataa
Dim Flag As Integer


'最上位に検索終了文字を追加(不必要?)
temp_data = "\" & temp_data

'配列に各bitの値を格納
For i = 1 To BitLength
    temp = Left(Right(temp_data, i), 1)
    If temp = "\" Then
        data(i) = 0
    Else
        data(i) = temp
    End If
Next

'値が負(最上位bitが1)の場合,正負逆転+2の補数
If data(BitLength) = 1 Then
    SignBit = 1
    Flag = 1    '2の補数による桁上がりフラグ
    For i = 1 To BitLength
        If data(i) = 0 Then
            data(i) = 1
            If Flag = 1 Then
                data(i) = 0
                Flag = 1
            End If
        Else
            data(i) = 0
            If Flag = 1 Then
                data(i) = 1
                Flag = 0
            End If
        End If
    Next
End If

' 整数部の値を計算
For i = (DecBit + 1) To BitLength
    IntNum = IntNum + data(i) * 2 ^ (i - (DecBit + 1))
Next

' 小数部の値を計算
For i = DecBit To 1 Step -1
    DicNum = DicNum + (data(i) * 2 ^ (i - (DecBit + 1)))
Next

' 整数部と小数部の結合
Result = IntNum + DicNum

' 値が負の場合は正負を逆転させる
If SignBit = 1 Then
    Result = -Result
End If

FixedPoint2DEC = Result

End Function

  • 10進数表記→2進数表記変換
'==================================================
' 実数(10進数)を固定小数点(2進数,符号付き32bit)へ変換
' 符号部1bit,整数部20bit,小数部11bit(bit幅は調整可能)
' Input  : -1.50473253979636
' Outpot : 111111111111111111110.01111110111(ピリオド.は確認用)
'==================================================
Function DEC2FixedPoint(dataa As Double) As String

Dim IntBit As Integer  ' 整数部のbit幅
Dim DecBit As Integer  ' 小数部のbit幅
Dim SignBit As Long    ' 符号部のbit
Dim BitLength As Long  ' bit幅
IntBit = 20
DecBit = 11
BitLength = IntBit + DecBit + 1

Dim IntNum As Double
Dim DecNum As Double
Dim IntOut As String
Dim DecOut As String
Dim i As Integer

Dim DecTemp As Double
Dim Flag As Integer

' 整数部を格納
IntNum = Fix(dataa)
' 小数部を格納
DecNum = Abs(dataa - Fix(dataa))

' 小数部を2進数表示
For i = 1 To DecBit
    If DecNum = 0 Then ' 終了条件
        DecOut = DecOut + "0"
    Else
        DecNum = DecNum * 2 '左へ1bitシフト
        If DecNum >= 1 Then
            DecOut = DecOut + "1"
            DecNum = DecNum - 1
        Else
            DecOut = DecOut + "0"
        End If
    End If
Next i

' 負の場合の処理
If Sgn(dataa) < 0 Then
    For i = 1 To DecBit
        DecTemp = DecTemp + CInt(Left(Right(DecOut, i), 1)) * 2 ^ (i - 1) ' 整数として表現
    Next
    DecTemp = ((2 ^ (DecBit + 1)) - 1) - DecTemp ' 正負逆転,2の補数
    DecTemp = DecTemp + 1
    If DecTemp >= (2 ^ (DecBit + 1)) Then
        Flag = 1
    End If
    DecOut = ""
    For i = DecBit - 1 To 0 Step -1
        SignBit = DecTemp And (2 ^ i)
        If SignBit <> 0 Then
            DecOut = DecOut & "1"
        Else
            DecOut = DecOut & "0"
        End If
    Next
End If

' 参考:http://okwave.jp/qa/q858647.html
' 整数部を2進数表示
If Sgn(dataa) >= 0 Then
    IntNum = CLng(IntNum)
Else
    ' 正負逆転
    IntNum = ((2 ^ (IntBit + 1)) - 1) + CLng(IntNum)
    ' 小数部の第1桁の桁上がり
    If Flag = 1 Then
        IntNum = IntNum + 1
    End If
End If
'MsgBox "IntNum: " & IntNum
For i = 1 To (IntBit + 1)
    SignBit = IntNum And (2 ^ IntBit) '最上位bitが1か0か判定
    If SignBit <> 0 Then
        IntOut = IntOut & "1"
    Else
        IntOut = IntOut & "0"
    End If
        IntNum = IntNum * 2 '左へ1bitシフト
    If IntNum >= ((2 ^ (IntBit + 1)) - 1) Then 'オーバーフロー防止の為、符号無しの&111111..でマスク
        IntNum = IntNum - ((2 ^ (IntBit + 1)) - 1)
    End If
Next
'MsgBox "IntOut: " & IntOut

'DEC2FixedPoint = IntOut + "." + DecOut
DEC2FixedPoint = IntOut + DecOut

End Function

Verilog

テストベンチ上で32bitの出力を10進数の実数に変換,デバッグの容易化を狙いとして作成.taskとして実装.

  • 2進数表記→10進数表記変換
parameter IntBit = 20;
parameter DecBit = 11;

task FixedPoint2DEC ( input [31:0]DATA_A );
reg [31:0] DATA;
reg SignBit;
real IntNum;
real DecNum;
real Result;
begin
if ( DATA_A[31]==1'b1 ) begin
    DATA = ( ~DATA_A + 1'b1 );
    SignBit = 1'b1;
end
else begin
    DATA = DATA_A;
    SignBit = 1'b0;
end
for ( i=DecBit; i<32; i = i+1) begin
    IntNum = IntNum + DATA[i] * ( 2 ** (i - (DecBit + 1)) );
end
for ( i=DecBit-1; i>=0; i = i-1) begin
    DecNum = DecNum + (DATA[i] * 2 ^ (i - (DecBit + 1)));
end
Result = IntNum + DecNum;
if (SignBit==1'b1) Result = -Result;
$display("DATA_A  :\t%b\t%f", DATA_A, result);
end
endtask
  • 10進数表記→2進数表記変換
    必要ないので未実装.

C言語

FPGA上に構築したNios2に載せる制御用プログラム,そのプログラム上で結果を確認するために利用.

  • 2進数表記→10進数表記変換
#define IntNumBit 20
#define DecNumBit 11
double FixedPoint2DEC(unsigned int x)
{
    int temp_data;
    temp_data = (int)x;

    double IntNum;
    double DecNum;
    double Result;
    int data[32];
    int i;
    int SignBit;
    int Flag;
    IntNum=0;
    DecNum=0;

    for(i=0;i<32;i++){
        data[i] = temp_data>>i & 1;
    }
    if(data[31]==1){
        SignBit = 1;
        Flag = 1;
        for(i=0;i<32;i++){
            if(data[i]==0){
                data[i] = 1;
                if(Flag==1){
                    data[i] = 0;
                    Flag = 1;
                }
            }else{
                data[i] = 0;
                if(Flag==1){
                    data[i] = 1;
                    Flag = 0;
                }
            }
        }
    }
    for(i=DecNumBit;i<32;i++){
        IntNum += data[i] * (int)cal_power(2, i-DecNumBit);
    }
    for(i=DecNumBit-1;i>=0;i--){
        DecNum += (double)data[i] * cal_power(0.5, DecNumBit-i);
    }
    Result = IntNum + DecNum;
    if(SignBit==1) Result *= -1;

    return Result;
}
  • 10進数表記→2進数表記変換
    必要ないので未実装.

参考リンク

10進数(+-)→2進数への変換(2) 【OKWave】

参考書籍