浮動小数点数の乗算
この記事はkivantium Advent Calendarの13日目です。
昨日は浮動小数点数のフォーマットについて説明しました。
今日は浮動小数点数の乗算をやります。
仕組み
をやれば計算できることになります。
入力を正規化数のみの計算に絞って、最近接丸めを採用したコードは以下のようになります。
module testbench; logic clk, start; logic [31:0] a, b, c; int i; fmul fm(.a(a), .b(b), .c(c)); initial begin a = 32'b01000000010010001111010111000011; // 3.14 b = 32'b00111101110011001100110011001101; // 0.1 start = 1'b1; clk = 1'b0; for(i=0; i<5; i=i+1) begin clk = 1'b1; #10; clk=1'b0; #10; end $display("%b %b %b", c[31], c[30:23], c[22:0]); $finish; end endmodule module fmul( input [31:0] a, b, output logic [31:0] c ); logic c_sign; logic [7:0] c_exp; logic [23:0] c_frac; assign c = {c_sign, c_exp, c_frac[22:0]}; logic [8:0] c_exp_0, c_exp_1; logic [47:0] c_frac_0; // 48bit = 2 * 24bit logic [23:0] c_frac_1; // 24bit logic guard_bit, round_bit, sticky_bit; logic guard_bit1, round_bit1, sticky_bit1; always @* begin c_sign = a[31] ^ b[31]; c_exp_0 = a[30:23] + b[30:23]; c_frac_0 = {1'b1, a[22:0]} * {1'b1, b[22:0]}; guard_bit = c_frac_0[23]; round_bit = c_frac_0[22]; sticky_bit = |c_frac_0[21:0]; if(c_frac_0[47] == 1'b1 || &c_frac[46:24] == 1'b1) begin c_frac_1 = c_frac_0[46:23]; c_exp_1 = c_exp_0 + 1; sticky_bit1 = sticky_bit; round_bit1 = round_bit; end else begin c_frac_1 = c_frac_0[47:24]; c_exp_1 = c_exp_0 + 1; sticky_bit1 = sticky_bit | round_bit; round_bit1 = guard_bit; end if(c_exp_1 < 127) begin c_exp = {9{1'b0}}; end else begin c_exp = c_exp_0 - 127; end if(round_bit1 == 1'b1 && (sticky_bit1 == 1'b1 || c_frac_1[0] == 1'b1)) begin c_frac = c_frac_1 + 1; end else begin c_frac = c_frac_1; end end endmodule
出力は
0 01111101 01000001100010010011100
となって昨日のプログラムで求めた3.14*0.1
の答えと一致しました。
丸めを適当にやっていて、Inf, NaNにも対応していないひどいコードなので後日ちゃんとしたものを書きます。