反量化Q8零.h
cpp
// 反量化Q8零.h ------ q8_0 量化块的反量化(8 位量化,无零偏移)
// 用途:把 q8_0 块(1 个 f16 缩放 + 32 个 int8 量化值)转为 f32 输出。
// 布局(实测确认):每块 32 元素、34 字节:d(f16, 2B) + q[32×int8, 32B]
#pragma once
// 引入基础类型(浮点/索引)
#include "公共/基础定义.h"
// 半精度转浮点:把 IEEE-754 半精度(16 位)位模式转为单精度浮点
// 说明:GGUF 文件里的缩放是 IEEE-754 半精度(1 符号位 + 5 指数位 + 10 尾数位),
// C++ 不保证有原生的 _Float16,故用手动位操作实现(见 .cpp 内详解)
浮点 半精度转浮点(uint16_t 位);
// 反量化Q8零块:把一个 q8_0 量化块反量化为 f32
// 参数:块 = 量化数据起点(34 字节);输出 = f32 输出缓冲区(容量 ≥ 元素数);元素数 = 32
// 公式(文档注释):x_i = q_i · d,其中 q_i 是第 i 个 int8 量化值,d 是块缩放
void 反量化Q8零块(const uint8_t* 块, float* 输出, size_t 元素数);
反量化Q8零.cpp
cpp
// 反量化Q8零.cpp ------ q8_0 量化块的反量化实现
// 用途:把 q8_0 块(1 个 f16 缩放 + 32 个 int8 量化值)转为 f32 输出。
// 布局:d(f16, 2B) + 32×int8 = 34 字节;每块 32 元素;x = q·d
#include "内核/反量化/反量化Q8零.h"
// 半精度转浮点:把 IEEE-754 半精度(16 位)位模式转为单精度浮点
// IEEE-754 半精度布局:1 符号位 | 5 指数位 | 10 尾数位
// - 指数 = 0 且尾数 = 0:±0
// - 指数 = 0 且尾数 ≠ 0:次正规数,值 = (-1)^符号 × 尾数 × 2^-24
// - 指数 = 31 且尾数 = 0:±∞
// - 指数 = 31 且尾数 ≠ 0:NaN(非数)
// - 其余:正规数,值 = (-1)^符号 × (1 + 尾数/1024) × 2^(指数-15)
浮点 半精度转浮点(uint16_t 位) {
// 符号位:第 15 位
const 无符号 符号 = (位 >> 15) & 1;
// 指数:第 14~10 位
const 无符号 指数 = (位 >> 10) & 0x1F;
// 尾数:第 9~0 位
const 无符号 尾数 = 位 & 0x3FF;
// 指数 0:零或次正规数(值 = 尾数 × 2^-24)
if (指数 == 0) {
// 5.9604645e-8 = 2^-24(单精度下的次正规最小步长)
return 符号 ? -尾数 * 5.9604645e-8f : 尾数 * 5.9604645e-8f;
}
// 指数 31:无穷或 NaN
if (指数 == 31) {
if (尾数 == 0) {
return 符号 ? -std::numeric_limits<浮点>::infinity()
: std::numeric_limits<浮点>::infinity();
}
return std::numeric_limits<浮点>::quiet_NaN();
}
// 正规数:(1 + 尾数/1024) × 2^(指数-15),符号另乘
const 浮点 值 = (1.0f + 尾数 / 1024.0f) * std::pow(2.0f, static_cast<int>(指数) - 15);
return 符号 ? -值 : 值;
}
// 反量化Q8零块:把一个 q8_0 量化块反量化为 f32
// 实现:读前 2 字节为 f16 缩放 d(小端),随后 32 字节为 int8 量化值;
// 对每个元素 x_i = q_i × d(int8 先有符号扩展到 f32 再乘)
void 反量化Q8零块(const uint8_t* 块, float* 输出, size_t 元素数) {
// 解析 f16 缩放:小端两字节拼成 16 位模式
const uint16_t 缩放位 = static_cast<uint16_t>(块[0] | (块[1] << 8));
const 浮点 d = 半精度转浮点(缩放位);
// 逐个元素反量化:q_i(int8 有符号)× d
for (size_t i = 0; i < 元素数; ++i) {
const int8_t q = static_cast<int8_t>(块[2 + i]);
输出[i] = static_cast<浮点>(q) * d;
}
}