IEEE 754单精度浮点数(binary32)使用32位编码:
| 字段 | 位宽 | 位位置 | 含义 |
|---|---|---|---|
| S (Sign) | 1 | [31] | 0=正数, 1=负数 |
| E (Exponent) | 8 | [30:23] | 偏移码,Bias=127 |
| M (Mantissa) | 23 | [22:0] | 小数部分(隐含1.或0.) |
| 项目 | 指数E | 实际指数 | 值 |
|---|---|---|---|
| 最小规格化数 | 1 | -126 | ±1.0...0 × 2^(-126) ≈ ±1.175×10^(-38) |
| 最大规格化数 | 254 | +127 | ±1.1...1 × 2^(+127) ≈ ±3.403×10^(+38) |
| 1.0 | 127 | 0 | ±1.0 |
| 2.0 | 128 | 1 | ±2.0 |
单精度有23位尾数 + 1位隐含位 = 24位有效数字
ULP是浮点数可表示的最小间距。对于指数为e的规格化数:
当E=127(值为1.0附近)时,ULP = 2^(-23) ≈ 1.19×10^(-7)
当E=200(值为2^73附近)时,ULP = 2^(50) ≈ 1.13×10^15 ——巨大!
//=============================================================
// float32_decoder.sv - IEEE 754 单精度浮点数解码器
// 功能:将32位浮点数解码为符号、实际指数、尾数(含隐含位)
// 验证:Verilator --lint-only
//=============================================================
module float32_decoder (
input wire [31:0] fp_in,
output wire sign, // 符号位
output wire [7:0] exp_field, // 原始指数字段
output wire [22:0] mant_field, // 原始尾数字段
output wire [7:0] exp_actual, // 实际指数(无偏)
output wire [23:0] mant_full, // 完整尾数(含隐含位)
output wire is_normal, // 是否规格化
output wire is_denorm, // 是否非规格化
output wire is_special // 是否特殊值(Inf/NaN)
);
// 字段提取
assign sign = fp_in[31];
assign exp_field = fp_in[30:23];
assign mant_field = fp_in[22:0];
// 分类判断
wire exp_is_zero = (exp_field == 8'b0);
wire exp_is_max = (exp_field == 8'hFF);
assign is_normal = ~exp_is_zero & ~exp_is_max;
assign is_denorm = exp_is_zero & (mant_field != 23'b0);
assign is_special = exp_is_max;
// 实际指数计算
// 规格化数: E - 127
// 非规格化数: -126 (不是-127!)
assign exp_actual = is_normal ? (exp_field - 8'd127) :
8'd130; // -126的无符号表示(用于显示)
// 完整尾数(含隐含位)
// 规格化数: 1.M → {1, mant_field}
// 非规格化数: 0.M → {0, mant_field}
assign mant_full = is_normal ? {1'b1, mant_field} :
{1'b0, mant_field};
endmodule
//=============================================================
// tb_float32_decoder.sv - 解码器测试
//=============================================================
module tb_float32_decoder;
reg [31:0] fp_in;
wire sign;
wire [7:0] exp_field;
wire [22:0] mant_field;
wire [7:0] exp_actual;
wire [23:0] mant_full;
wire is_normal, is_denorm, is_special;
float32_decoder uut (
.fp_in(fp_in), .sign(sign), .exp_field(exp_field),
.mant_field(mant_field), .exp_actual(exp_actual),
.mant_full(mant_full), .is_normal(is_normal),
.is_denorm(is_denorm), .is_special(is_special)
);
integer errors = 0;
task check_normal;
input [31:0] val;
input exp_sign;
input [7:0] exp_val;
input [23:0] mant_val;
input [255:0] name;
begin
fp_in = val;
#10;
if (sign !== exp_sign || exp_actual !== exp_val || mant_full !== mant_val || !is_normal) begin
$display("FAIL %0s: sign=%b exp=%0d mant=%h norm=%b",
name, sign, exp_actual, mant_full, is_normal);
errors = errors + 1;
end else
$display("PASS %0s", name);
end
endtask
initial begin
// 1.0: 0x3F800000, exp=0, mant=1.000...
check_normal(32'h3F80_0000, 1'b0, 8'd0, 24'h800000, "1.0");
// 2.0: 0x40000000, exp=1, mant=1.000...
check_normal(32'h4000_0000, 1'b0, 8'd1, 24'h800000, "2.0");
// -6.625: 0xC0D40000, exp=2, mant=1.10101...
check_normal(32'hC0D4_0000, 1'b1, 8'd2, 24'hD40000, "-6.625");
// 10.0: 0x41200000, exp=3, mant=1.01...
check_normal(32'h4120_0000, 1'b0, 8'd3, 24'hA00000, "10.0");
// 特殊值测试
fp_in = 32'h7F80_0000; #10;
if (!is_special) begin $display("FAIL +Inf not special"); errors = errors + 1; end
else $display("PASS +Inf is special");
fp_in = 32'h0000_0001; #10;
if (!is_denorm) begin $display("FAIL MinDenorm"); errors = errors + 1; end
else $display("PASS MinDenorm is denorm");
$display("\n=== Errors: %0d ===", errors);
$finish;
end
endmodule
PASS 1.0
PASS 2.0
PASS -6.625
PASS 10.0
PASS +Inf is special
PASS MinDenorm is denorm
=== Errors: 0 ===
✅Verilator验证通过 ✅6/6测试通过
这是浮点数最经典的问题之一。让我们从单精度格式来理解:
这不是"bug",而是有限精度表示的固有特性。在FPU设计中,理解这一点对于舍入逻辑至关重要。
单精度浮点数有一个有趣的性质:对于正规格化数,如果把浮点数的位模式当作无符号整数,则整数的大小关系等价于浮点数的大小关系。
练习1:手动计算 π ≈ 3.14159265 的单精度表示,验证你的结果。
练习2:0x42F60000 表示什么值?
练习3:实现一个Verilog模块,将单精度浮点数的ULP(最小表示间距)计算出来。
练习4:写一个测试,验证"0.1 + 0.2 ≠ 0.3"在浮点数中确实成立。
✅ 掌握了单精度格式的每个位含义
✅ 能手动进行十进制↔单精度转换
✅ 理解了精度极限与ULP概念
✅ 实现了单精度解码器
下一成就:双精度大师 — 下一课将深入双精度格式