<p>C# 中 System.Half 自 .NET 5 起引入但非默认可用,需目标框架 ≥ net5.0、using System.Runtime.InteropServices,且依赖平台支持;构造与转换必须显式,无隐式转换;算术运算多为软件模拟,性能通常不如 float。</p>

C# 标准语言层不支持 Half 类型,也没有内置的 System.Half(.NET 6 及以前版本),直接写 Half x = 1.0f; 会编译失败。真要用半精度浮点数,得看运行时环境和目标框架是否提供支持。
System.Half 在 .NET 5+ 中是可选类型,但需注意平台兼容性
从 .NET 5 开始,System.Half 被引入为一个结构体,但它不是“默认可用”的:
- 它仅在 System.Runtime.InteropServices 命名空间下,且依赖底层硬件或运行时对半精度的支持
- .NET 6+ 在 Windows x64、Linux x64、macOS ARM64 等主流平台已包含该类型定义,但某些嵌入式或旧版 AOT 编译场景可能缺失
- 即使类型存在,Half 的算术运算(如 +、*)仍由 JIT 或运行时模拟,不保证生成原生 VCVTPS2PH 指令
常见错误现象:
- 编译报错
The type or namespace name 'Half' could not be found→ 缺少System.Runtime.InteropServicesusing 或目标框架低于 .NET 5 - 运行时报
PlatformNotSupportedException→ 当前平台未实现Half的底层转换逻辑(例如某些 .NET Native AOT 配置)
使用前务必检查:
- 项目 SDK 必须为
<TargetFramework>net5.0</TargetFramework>或更高 - 添加
using System.Runtime.InteropServices; - 用
typeof(Half).IsSupported(.NET 7+)或尝试构造再捕获异常来运行时探活
Half 构造与转换必须显式,隐式转换全部被禁用
Half 不允许从 float、double 或字面量隐式转换,这是为了防止无意识的精度丢失。所有转换都需调用构造函数或静态方法:
- new Half(3.14f) → 从 float 构造(舍入到最近可表示的 Half)
- Half.FromBytes(new byte[]{0x00, 0x3C}) → 从 IEEE754 FP16 字节序列还原(小端)
- halfValue.ToSingle() → 显式转回 float
容易踩的坑:
- 写
Half h = 0.5f;直接编译失败,必须写成Half h = new Half(0.5f); -
0.1f转Half后再转回float,结果可能是0.1001f或0.0996f,因为 FP16 尾数只有 10 位(含隐藏位),远低于 FP32 的 23 位 - 使用
Half.MaxValue(≈65504)或Half.Epsilon(≈0.000061035)前,先确认数值没溢出或下溢
算术运算性能不可控,别假设它比 float 快
虽然 FP16 在 GPU 或 AI 加速器上常用于吞吐优化,但在 CPU 上用 Half 做密集计算,往往反而更慢:
- .NET 的 Half 运算基本靠软件模拟:先转 float → 计算 → 再转回 Half
- 没有 JIT 内联优化,每次加法都有至少两次装箱/解包开销
- 数组操作(如 Half[])虽能节省内存带宽(一半大小),但访问时仍要频繁转换
实操建议:
- 仅在明确需要压缩数据体积(如模型权重序列化、网络传输)时用
Half存储 - 计算逻辑一律用
float或double,只在 I/O 边界做一次转换 - 若真需高性能 FP16 计算,应调用平台原生 API(如 Windows 的
DirectML、Linux 的oneDNN),而非依赖System.Half
序列化和跨语言交互时字节序与格式必须对齐
Half 的二进制布局严格遵循 IEEE 754-2008 FP16 格式(1-5-10),但不同系统对字节序处理不一致:
- Half.ToBytes() 返回小端排列的两个字节
- Python 的 numpy.float16 默认也是小端,但若用 struct.unpack('<H', ...) 解包需确认原始数据确实是小端
- C/C++ 中若用 uint16_t 强转,必须确保平台字节序与 C# 一致,否则高低字节颠倒
关键检查点:
- 用
BitConverter.IsLittleEndian确认当前环境字节序 - 与 C 代码交互时,避免直接 memcpy
Half实例,改用ToBytes()+ 显式字节操作 - JSON 序列化默认把
Half当作数字(即调用ToSingle()),丢失半精度特性;如需保留,必须自定义JsonConverter<Half>
FP16 的真正价值不在通用计算,而在存储密度和特定加速器适配。C# 里的 System.Half 是个“能用但别滥用”的工具——它帮你省了内存,却未必帮你省时间。


















