如何将整数常量作为泛型参数传递?附非标准浮点数处理代码示例
如何在Delphi中将整数常量作为泛型参数传递?
我懂你现在的需求——处理不同规格的非标准浮点数时,总不想写一堆几乎一模一样的函数吧?就像你贴的这段处理float16的代码,ES(指数位宽)、MS(尾数位宽)都是硬编码的常量,要是换个规格的浮点数,就得复制代码改常量,太麻烦了。咱们可以用泛型把这些整数常量变成参数,让一个函数适配多种情况。
先看看你给出的原始代码:
const ES = 5; // exponent size (bits) MS = 10; // mantissa size (bits) ESMS = ES + MS; function FloatXXToSingle(const _in: cardinal): Single; var t1, t2, t3: cardinal; begin t1 := _in and (Cardinal(1 shl ESMS) - 1); // Non-sign bits t2 := _in and (1 shl ESMS); // Sign bit // 这里省略后续的转换逻辑... end;
这段代码只能处理ES=5、MS=10的float16,要是想处理其他规格,就得重复写函数。下面给你两种实用的解决方案,覆盖不同Delphi版本:
方案1:泛型记录+编译期常量(兼容旧版Delphi)
如果你的Delphi版本低于11,咱们可以用泛型记录来封装常量和转换函数,编译器会在编译期把常量值代入,完全没运行时开销:
type // 用泛型参数传递ES和MS的常量值 TFloatConverter<ESConst, MSConst> = record public const ES = ESConst; MS = MSConst; ESMS = ES + MS; // 衍生常量也能直接用 class function ConvertToSingle(const _in: Cardinal): Single; static; end; class function TFloatConverter<ESConst, MSConst>.ConvertToSingle(const _in: Cardinal): Single; var t1, t2, t3: Cardinal; begin t1 := _in and (Cardinal(1 shl ESMS) - 1); // 取非符号位 t2 := _in and (1 shl ESMS); // 取符号位 // 这里补上你原来的转换逻辑,比如调整位宽到标准float32 t3 := (t1 shl (8 - ES)) or (t2 shl (31 - ESMS)); Result := PSingle(@t3)^; end;
用的时候直接指定常量就行,比如处理float16:
var Float16Data: Cardinal; StandardFloat: Single; begin Float16Data := $3C00; // 示例float16值,对应0.5 StandardFloat := TFloatConverter<5, 10>.ConvertToSingle(Float16Data); end;
要是要处理另一种规格,比如ES=8、MS=23的标准float32(只是示例),直接改泛型参数:TFloatConverter<8,23>.ConvertToSingle(...),不用改函数代码。
方案2:直接用数值泛型参数(Delphi 11+)
从Delphi 11 Alexandria开始,官方支持把整数、枚举这类数值类型作为泛型参数,写法更简洁:
type // 直接约束泛型参数为整数类型 TFloatConverter<ES: Integer; MS: Integer> = record public class function ConvertToSingle(const _in: Cardinal): Single; static; end; class function TFloatConverter<ES, MS>.ConvertToSingle(const _in: Cardinal): Single; const ESMS = ES + MS; // 编译期计算常量 var t1, t2, t3: Cardinal; begin t1 := _in and (Cardinal(1 shl ESMS) - 1); t2 := _in and (1 shl ESMS); // 转换逻辑和上面一致 t3 := (t1 shl (8 - ES)) or (t2 shl (31 - ESMS)); Result := PSingle(@t3)^; end;
使用方式和方案1完全一样,编译器会自动把ES和MS当作编译期常量处理,效率拉满。
小提醒
- 一定要用编译期常量作为泛型参数,比如直接写
5、10,或者定义的const常量,别传变量,不然编译器没法做优化。 - 位运算的时候注意类型溢出,用
UInt32代替Cardinal会更明确,避免意外问题。
内容的提问来源于stack exchange,提问作者Dmitro25
相关产品推荐
相关产品推荐

