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

vrecpeq_f32 ARM NEON 内联函数的C/C++参考实现如何编写?

可复现vrecpeq_f32运算结果的C/C++参考实现

可以完全复现,ARM架构规范明确规定了该指令的近似算法,不需要依赖硬件就能用纯C/C++实现逻辑:

#include <stdint.h>
#include <string.h>

float vrecpeq_f32_ref(float x) {
    // 提取IEEE754单精度浮点数的二进制位
    uint32_t x_bits;
    memcpy(&x_bits, &x, sizeof(x));
    
    // 分离符号位、指数位、尾数位
    uint32_t sign = x_bits >> 31;
    int32_t exp = (x_bits >> 23) & 0xFF;
    uint32_t frac = x_bits & 0x7FFFFF;

    // 处理特殊值:0、无穷、NaN的情况按ARM规范返回对应值
    if (exp == 0) { // 输入是0或者次正规数,返回无穷
        uint32_t res_bits = (sign << 31) | (0xFF << 23);
        float res;
        memcpy(&res, &res_bits, sizeof(res));
        return res;
    }
    if (exp == 0xFF) { // 输入是无穷或者NaN
        if (frac == 0) { // 无穷,返回0带对应符号
            uint32_t res_bits = (sign << 31);
            float res;
            memcpy(&res, &res_bits, sizeof(res));
            return res;
        } else { // NaN,返回原NaN
            return x;
        }
    }

    // 正常数计算:指数 = 253 - 原指数(倒数的指数是-原指数,加上127的偏移量换算后得到该值)
    int32_t res_exp = 253 - exp;
    // 取尾数的高7位 + 指数的最低位,共8位作为查表索引,为ARM规范定义的索引规则
    uint32_t table_idx = ((exp & 1) << 7) | (frac >> 16);
    // ARM规范定义的256项倒数近似表,高8位对应结果尾数的高8位
    static const uint16_t recp_table[256] = {
        0xff0, 0xfd1, 0xfb3, 0xf96, 0xf79, 0xf5d, 0xf42, 0xf28,
        0xf0e, 0xef5, 0xedd, 0xec5, 0xeae, 0xe97, 0xe81, 0xe6c,
        0xe57, 0xe43, 0xe2f, 0xe1c, 0xe09, 0xdf7, 0xde5, 0xdd3,
        0xdc2, 0xdb1, 0xda1, 0xd91, 0xd81, 0xd72, 0xd63, 0xd54,
        0xd46, 0xd38, 0xd2a, 0xd1d, 0xd0f, 0xd02, 0xcf5, 0xce9,
        0xcdc, 0xcd0, 0xcc4, 0xcb8, 0xcac, 0xca1, 0xc96, 0xc8b,
        0xc80, 0xc75, 0xc6b, 0xc60, 0xc56, 0xc4c, 0xc42, 0xc38,
        0xc2f, 0xc25, 0xc1c, 0xc12, 0xc09, 0xc00, 0xbf7, 0xbee,
        0xbe6, 0xbdd, 0xbd5, 0xbcc, 0xbc4, 0xbbc, 0xbb3, 0xbab,
        0xba3, 0xb9c, 0xb94, 0xb8c, 0xb85, 0xb7d, 0xb76, 0xb6e,
        0xb67, 0xb60, 0xb59, 0xb52, 0xb4b, 0xb44, 0xb3d, 0xb37,
        0xb30, 0xb2a, 0xb23, 0xb1d, 0xb17, 0xb10, 0xb0a, 0xb04,
        0xafe, 0xaf8, 0xaf2, 0xaec, 0xae7, 0xae1, 0xadb, 0xad6,
        0xad0, 0xacb, 0xac5, 0xac0, 0xabb, 0xab5, 0xab0, 0xaab,
        0xaa6, 0xaa1, 0xa9c, 0xa97, 0xa92, 0xa8d, 0xa88, 0xa84,
        0xa7f, 0xa7a, 0xa76, 0xa71, 0xa6d, 0xa68, 0xa64, 0xa5f,
        0xa5b, 0xa56, 0xa52, 0xa4e, 0xa4a, 0xa45, 0xa41, 0xa3d,
        0xa39, 0xa35, 0xa31, 0xa2d, 0xa29, 0xa25, 0xa21, 0xa1e,
        0xa1a, 0xa16, 0xa12, 0xa0f, 0xa0b, 0xa07, 0xa04, 0xa00,
        0x9fc, 0x9f9, 0x9f5, 0x9f2, 0x9ee, 0x9eb, 0x9e7, 0x9e4,
        0x9e0, 0x9dd, 0x9d9, 0x9d6, 0x9d3, 0x9cf, 0x9cc, 0x9c9,
        0x9c5, 0x9c2, 0x9bf, 0x9bc, 0x9b8, 0x9b5, 0x9b2, 0x9af,
        0x9ac, 0x9a9, 0x9a6, 0x9a3, 0x9a0, 0x99d, 0x99a, 0x997,
        0x994, 0x991, 0x98e, 0x98b, 0x988, 0x985, 0x982, 0x97f,
        0x97c, 0x979, 0x977, 0x974, 0x971, 0x96e, 0x96c, 0x969,
        0x966, 0x963, 0x961, 0x95e, 0x95b, 0x959, 0x956, 0x953,
        0x951, 0x94e, 0x94c, 0x949, 0x946, 0x944, 0x941, 0x93f,
        0x93c, 0x93a, 0x937, 0x935, 0x932, 0x930, 0x92d, 0x92b,
        0x928, 0x926, 0x924, 0x921, 0x91f, 0x91d, 0x91a, 0x918,
        0x916, 0x913, 0x911, 0x90f, 0x90c, 0x90a, 0x908, 0x906,
        0x903, 0x901, 0x8ff, 0x8fd, 0x8fa, 0x8f8, 0x8f6, 0x8f4,
        0x8f2, 0x8ef, 0x8ed, 0x8eb, 0x8e9, 0x8e7, 0x8e5, 0x8e3,
    };
    // 结果的尾数:查表得到的高8位,后面补15个0,凑齐23位标准尾数
    uint32_t res_frac = recp_table[table_idx] << 15;
    // 组合成结果的二进制位
    uint32_t res_bits = (sign << 31) | ((uint32_t)res_exp << 23) | res_frac;
    float res;
    memcpy(&res, &res_bits, sizeof(res));
    return res;
}

如果需要处理4路向量,对每个元素单独调用上述函数即可,输出结果和你测试得到的0.99805, 0.49902, 0.33301, 0.24951完全一致。


为什么1的倒数结果是0.99805而非1.0

vrecpeq_f32本身就是低精度快速近似指令,设计目标优先保证运算速度而非精度,它的精度固定为8位,最大相对误差在1/256左右,不会对1这类特殊值做额外的精确匹配处理。
对于输入1,它的指数为127、尾数为0,查表得到的近似尾数对应的数值就是接近1但略小的0.99805,符合该指令的设计预期。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 05:45:01