You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C语言中float32与float16(uint16_t)互转及边缘情况处理

C语言实现float32与float16(uint16_t存储)双向转换(含边缘情况处理)

核心原理

先明确两种浮点数的二进制格式:

  • float32:1位符号位 + 8位指数位(偏移量127) + 23位尾数位
  • float16:1位符号位 + 5位指数位(偏移量15) + 10位尾数位

我们用uint16_t存储float16的原始二进制位,无需进行算术运算,仅保证二进制表示完全符合float16标准。

实现代码

依赖头文件

#include <stdint.h>
#include <stdbool.h>

辅助宏与类型定义

typedef uint16_t float16_t;

// float32字段提取宏
#define F32_SIGN_MASK 0x80000000U
#define F32_EXP_MASK  0x7F800000U
#define F32_MANT_MASK 0x007FFFFFU
#define F32_EXP_SHIFT 23U
#define F32_EXP_BIAS  127U

// float16字段提取宏
#define F16_SIGN_MASK 0x8000U
#define F16_EXP_MASK  0x7C00U
#define F16_MANT_MASK 0x03FFU
#define F16_EXP_SHIFT 10U
#define F16_EXP_BIAS  15U

float32转float16(uint16_t)

float16_t float32_to_float16(float f) {
    union {
        float f32;
        uint32_t u32;
    } conv = {.f32 = f};

    uint32_t u32 = conv.u32;
    uint16_t sign = (u32 & F32_SIGN_MASK) >> 16;
    uint32_t exp32 = (u32 & F32_EXP_MASK) >> F32_EXP_SHIFT;
    uint32_t mant32 = u32 & F32_MANT_MASK;

    // 处理无穷大/NaN
    if (exp32 == 0xFF) {
        if (mant32 == 0) {
            // 无穷大:指数全1,尾数0
            return sign | 0x7C00U;
        } else {
            // NaN:保留尾数高10位,强制转为QNaN(避免陷阱)
            uint16_t mant16 = (mant32 >> 13) | 0x0200U;
            return sign | 0x7C00U | mant16;
        }
    }

    // 处理正负零
    if (exp32 == 0 && mant32 == 0) {
        return sign;
    }

    // 处理正常数值
    int32_t exp16 = exp32 - F32_EXP_BIAS + F16_EXP_BIAS;

    if (exp16 <= 0) {
        // 下溢:转为非规格化数或零
        if (exp16 <= -10) {
            return sign;
        }
        uint32_t mant_shifted = mant32 | (1U << 23);
        uint16_t mant16 = mant_shifted >> (23 + 1 - exp16);
        return sign | mant16;
    } else if (exp16 >= 0x1F) {
        // 上溢:转为无穷大
        return sign | 0x7C00U;
    } else {
        // 规格化数:截断尾数到10位(如需四舍五入可取消下方注释)
        uint16_t mant16 = mant32 >> 13;
        // 四舍五入逻辑:
        // if ((mant32 & 0x1FFFU) > 0x1000U) mant16 += 1;
        // if (mant16 & 0x0400U) { mant16 = 0; exp16 += 1; }
        return sign | ((uint16_t)exp16 << F16_EXP_SHIFT) | mant16;
    }
}

float16转float32

// 手动实现前导零计数(替代GCC内置__builtin_clz,兼容WebAssembly)
static int count_leading_zeros(uint16_t x) {
    if (x == 0) return 16;
    int cnt = 0;
    while (!(x & 0x8000U)) {
        cnt++;
        x <<= 1;
    }
    return cnt;
}

float float16_to_float32(float16_t f16) {
    union {
        float f32;
        uint32_t u32;
    } conv;

    uint16_t sign = f16 & F16_SIGN_MASK;
    uint16_t exp16 = (f16 & F16_EXP_MASK) >> F16_EXP_SHIFT;
    uint16_t mant16 = f16 & F16_MANT_MASK;

    uint32_t u32 = 0;

    // 处理无穷大/NaN
    if (exp16 == 0x1F) {
        u32 |= (uint32_t)sign << 16;
        u32 |= 0x7F800000U;
        if (mant16 != 0) {
            u32 |= (uint32_t)mant16 << 13;
        }
        conv.u32 = u32;
        return conv.f32;
    }

    // 处理正负零
    if (exp16 == 0 && mant16 == 0) {
        u32 |= (uint32_t)sign << 16;
        conv.u32 = u32;
        return conv.f32;
    }

    // 处理正常数值
    if (exp16 == 0) {
        // 非规格化数转换
        int shift = count_leading_zeros(mant16) - 6;
        int32_t exp32 = F32_EXP_BIAS - F16_EXP_BIAS + 1 - shift;
        uint32_t mant32 = (uint32_t)mant16 << shift;
        u32 |= (uint32_t)sign << 16;
        u32 |= (uint32_t)exp32 << F32_EXP_SHIFT;
        u32 |= mant32;
    } else {
        // 规格化数转换
        int32_t exp32 = exp16 - F16_EXP_BIAS + F32_EXP_BIAS;
        uint32_t mant32 = (uint32_t)mant16 << 13;
        u32 |= (uint32_t)sign << 16;
        u32 |= (uint32_t)exp32 << F32_EXP_SHIFT;
        u32 |= mant32;
    }

    conv.u32 = u32;
    return conv.f32;
}

关键边缘情况处理说明

  • 无穷大:严格对应两种格式的无穷大二进制表示,保留符号位
  • NaN:强制生成QNaN(尾数最高位为1),避免触发硬件陷阱,同时保留原始尾数的有效信息
  • 零:保留正负零的符号位,指数和尾数均置0
  • 下溢/上溢:float32超出float16范围时,分别转为零或无穷大
  • 非规格化数:正确处理两种格式之间的非规格化转换,保证二进制表示的一致性

内容的提问来源于stack exchange,提问作者juffma

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 08:37:17