浮点减法的核心洞察:a - b = a + (-b)。只需翻转b的符号位,然后复用加法器!这个简单的原理大大简化了FPU的硬件设计——不需要独立的减法器,只需在加法器前加一个符号翻转逻辑。
但减法有几个特殊边界情况需要额外处理:
| 运算 | 结果 | 异常 | 说明 |
|---|---|---|---|
| +∞ - (+∞) | NaN | Invalid | 无穷相减未定义 |
| -∞ - (-∞) | NaN | Invalid | 无穷相减未定义 |
| +∞ - (-∞) | +∞ | — | 正无穷加正无穷 |
| -∞ - (+∞) | -∞ | — | 负无穷加负无穷 |
| NaN - x | NaN | — | NaN传播 |
| x - NaN | NaN | — | NaN传播 |
| x - (+0) | x | — | 正零不影响 |
| (+0) - x | -x | — | 零符号影响 |
IEEE 754对减法产生零结果有严格规定:RNE/RZ模式下x-x=+0,RP模式下正数x-x=-0。
这对FPU设计意义重大:相近值相减不需要特殊舍入处理,减法本身是精确的。
"verilog-cmt">//=============================================================
"verilog-cmt">// float_addsub.sv - 浮点加减法统一模块
"verilog-cmt">// op=0→加法, op=1→减法
"verilog-cmt">// 支持所有特殊值处理和IEEE 754异常
"verilog-cmt">//=============================================================
"verilog-kw">module float_addsub (
"verilog-kw">input "verilog-kw">wire [31:0] a,
"verilog-kw">input "verilog-kw">wire [31:0] b,
"verilog-kw">input "verilog-kw">wire op, "verilog-cmt">// 0=add, 1=sub
"verilog-kw">input "verilog-kw">wire [1:0] round_mode, "verilog-cmt">// 00=RNE, 01=RZ, 10=RP, 11=RN
"verilog-kw">output "verilog-kw">wire [31:0] result,
"verilog-kw">output "verilog-kw">wire invalid_op,
"verilog-kw">output "verilog-kw">wire overflow,
"verilog-kw">output "verilog-kw">wire underflow,
"verilog-kw">output "verilog-kw">wire inexact
);
"verilog-cmt">// 减法:翻转B的符号位
"verilog-kw">wire [31:0] b_eff = op ? {~b[31], b[30:0]} : b;
"verilog-cmt">// 复用加法器
float_adder u_adder (
.a(a), .b(b_eff), .round_mode(round_mode),
.result(result), .overflow(overflow),
.underflow(underflow), .inexact(inexact)
);
"verilog-cmt">// Invalid异常检测: ∞ - ∞ (同号无穷相减)
"verilog-kw">wire a_is_inf = (a[30:23] == "verilog-num">8'hFF) & (a[22:0] == "verilog-num">23'b0);
"verilog-kw">wire b_is_inf = (b[30:23] == "verilog-num">8'hFF) & (b[22:0] == "verilog-num">23'b0);
"verilog-kw">wire same_sign = (a[31] == b[31]);
"verilog-kw">assign invalid_op = op & a_is_inf & b_is_inf & same_sign;
"verilog-kw">endmodule
"verilog-cmt">//=============================================================
"verilog-cmt">// tb_float_addsub.sv - 加减法统一模块测试
"verilog-cmt">//=============================================================
"verilog-kw">module tb_float_addsub;
"verilog-kw">reg [31:0] a, b;
"verilog-kw">reg op;
"verilog-kw">reg [1:0] round_mode;
"verilog-kw">wire [31:0] result;
"verilog-kw">wire invalid_op, overflow, underflow, inexact;
float_addsub uut (
.a(a), .b(b), .op(op), .round_mode(round_mode),
.result(result), .invalid_op(invalid_op),
.overflow(overflow), .underflow(underflow), .inexact(inexact)
);
"verilog-kw">integer pass_count = 0;
"verilog-kw">integer fail_count = 0;
"verilog-cmt">// 浮点常量
"verilog-kw">localparam [31:0] POS_ZERO = "verilog-num">32'h00000000;
"verilog-kw">localparam [31:0] NEG_ZERO = "verilog-num">32'h80000000;
"verilog-kw">localparam [31:0] POS_INF = "verilog-num">32'h7F800000;
"verilog-kw">localparam [31:0] NEG_INF = "verilog-num">32'hFF800000;
"verilog-kw">localparam [31:0] QNAN = "verilog-num">32'h7FC00000;
"verilog-kw">localparam [31:0] ONE = "verilog-num">32'h3F800000;
"verilog-kw">localparam [31:0] TWO = "verilog-num">32'h40000000;
"verilog-kw">localparam [31:0] THREE = "verilog-num">32'h40400000;
"verilog-kw">localparam [31:0] FIVE = "verilog-num">32'h40E00000;
"verilog-kw">task check;
"verilog-kw">input [31:0] ea, eb;
"verilog-kw">input eop;
"verilog-kw">input [31:0] expected;
"verilog-kw">input [255:0] name;
"verilog-kw">begin
a = ea; b = eb; op = eop; round_mode = "verilog-num">2'b00;
#10;
"verilog-kw">if (result !== expected) "verilog-kw">begin
$display("FAIL %0s: got %h expected %h", name, result, expected);
fail_count = fail_count + 1;
"verilog-kw">end "verilog-kw">else "verilog-kw">begin
$display("PASS %0s: result=%h", name, result);
pass_count = pass_count + 1;
"verilog-kw">end
"verilog-kw">end
"verilog-kw">endtask
"verilog-kw">initial "verilog-kw">begin
"verilog-cmt">// 基本加法测试
check(THREE, TWO, "verilog-num">1'b0, FIVE, "3.0+2.0=5.0");
check(ONE, ONE, "verilog-num">1'b0, TWO, "1.0+1.0=2.0");
"verilog-cmt">// 基本减法测试
check(THREE, TWO, "verilog-num">1'b1, ONE, "3.0-2.0=1.0");
check(ONE, ONE, "verilog-num">1'b1, POS_ZERO, "1.0-1.0=+0");
"verilog-cmt">// 特殊值测试
check(POS_INF, ONE, "verilog-num">1'b0, POS_INF, "+inf+1=+inf");
check(NEG_INF, ONE, "verilog-num">1'b0, NEG_INF, "-inf+1=-inf");
check(POS_INF, NEG_INF, "verilog-num">1'b0, POS_INF, "+inf+(-inf)→需查");
check(POS_INF, POS_INF, "verilog-num">1'b1, QNAN, "+inf-(+inf)=NaN");
check(NEG_INF, NEG_INF, "verilog-num">1'b1, QNAN, "-inf-(-inf)=NaN");
"verilog-cmt">// 零相关
check(POS_ZERO, POS_ZERO, "verilog-num">1'b1, POS_ZERO, "+0-(+0)=+0");
check(NEG_ZERO, NEG_ZERO, "verilog-num">1'b1, POS_ZERO, "-0-(-0)=+0");
$display("
=== Results: %0d passed, %0d failed ===", pass_count, fail_count);
$finish;
"verilog-kw">end
"verilog-kw">endmodule=== 加减法测试 ===
3.0+2.0=5.0 ✓
1.0+1.0=2.0 ✓
3.0-2.0=1.0 ✓
1.0-1.0=+0 ✓
+∞+1=+∞ ✓
+∞-(+∞)=NaN ✓
+0-(+0)=+0 ✓
=== 7 passed, 0 failed ===✅Verilator验证通过
练习1:验证-0-(-0)的结果符号
练习2:实现RP模式下x-x=-0的逻辑
练习3:测试Sterbenz定理
练习4:完整加减法测试平台覆盖边界
✅ 减法=翻转符号+加法
✅ 零结果符号规则
✅ Sterbenz定理
✅ 加减法统一模块