如何在F# 5.0中从decimal变量指针创建Span<int>?
在F# 5.0中无分配访问decimal内部表示的实现方法
问题背景
用户在C#中可通过以下无分配方式获取decimal的内部结构:
decimal v = 123.456M; var span = new ReadOnlySpan<int>(&v, 4); var flags = span[0]; var hi32 = span[1]; var lo32 = span[2]; var mi32 = span[3];
需求是在F# 5.0中实现相同逻辑,用于移植.NET 7的Decimal.IsCanonical方法到.NET Framework 4.7.2,且要求避免使用会分配数组的Decimal.GetBits()方法(因为用于热路径)。
可行实现方案
F# 5.0支持通过不安全代码结合指针或Span<T>实现无分配访问,以下是两种可靠方案:
方案1:借助ReadOnlySpan
open System let getDecimalBits (v: decimal) = unsafe { let ptr = &&v |> NativePtr.toVoidPtr let span = ReadOnlySpan<int>(ptr, 4) let flags = span[0] let hi32 = span[1] let lo32 = span[2] let mi32 = span[3] flags, hi32, lo32, mi32 }
方案2:直接使用指针操作
open System let getDecimalBits (v: decimal) = unsafe { fixed (decimal* p = &v) { let intPtr = p |> NativePtr.toVoidPtr :?> int* let flags = NativePtr.read intPtr let hi32 = NativePtr.read (NativePtr.add intPtr 1) let lo32 = NativePtr.read (NativePtr.add intPtr 2) let mi32 = NativePtr.read (NativePtr.add intPtr 3) flags, hi32, lo32, mi32 } }
关键注意事项
- 开启不安全代码:需在F#项目设置中勾选「允许不安全代码」,或编译时添加
--unsafe参数。 - 结构一致性:decimal的内部布局和C#完全一致,四个int的顺序对应标志位、高32位、低32位、中32位,可直接用于实现
IsCanonical的校验逻辑。 - 性能保障:两种方案均无堆分配,性能与C#实现持平,适合热路径场景。
内容的提问来源于stack exchange,提问作者Andrew Rybka
相关产品推荐
相关产品推荐

