如何将3个带符号4位整数(每数占5位)打包为单个16位整数
打包解包实现思路与代码
核心逻辑
每个带符号4位整数占5位存储空间(1位符号位+4位绝对值数据位,取值范围为*-15 ~ +15*),3个值总计占用15位,剩余1位可作为预留位存储自定义标识,刚好适配16位整数的存储空间。
位分配规则如下:
- 位0 ~ 位4:存储X分量,位0为符号位,位1~4为绝对值数据位
- 位5 ~ 位9:存储Y分量,位5为符号位,位6~9为绝对值数据位
- 位10 ~ 位14:存储Z分量,位10为符号位,位11~14为绝对值数据位
- 位15:预留位,默认置0
代码实现
依赖GLM库的glm::clamp、glm::sign、glm::abs等工具函数,代码如下:
#include <glm/glm.hpp> #include <cstdint> // 打包3个[-15, +15]范围内的有符号整数到16位uint uint16_t pack_3i4_to_uint16(const glm::ivec3& coord) { // 先将分量钳位到合法范围,避免越界 const glm::ivec3 clamped = glm::clamp(coord, glm::ivec3(-15), glm::ivec3(15)); uint16_t packed = 0; // 处理X分量 const uint16_t x_sign = (glm::sign(clamped.x) < 0) ? 1U : 0U; const uint16_t x_abs = static_cast<uint16_t>(glm::abs(clamped.x)); packed |= (x_sign) | (x_abs << 1); // 处理Y分量 const uint16_t y_sign = (glm::sign(clamped.y) < 0) ? 1U : 0U; const uint16_t y_abs = static_cast<uint16_t>(glm::abs(clamped.y)); packed |= (y_sign << 5) | (y_abs << 6); // 处理Z分量 const uint16_t z_sign = (glm::sign(clamped.z) < 0) ? 1U : 0U; const uint16_t z_abs = static_cast<uint16_t>(glm::abs(clamped.z)); packed |= (z_sign << 10) | (z_abs << 11); // 预留位默认置0,如有自定义需求可自行修改此处逻辑 return packed; } // 从16位uint解包出3个有符号整数 glm::ivec3 unpack_uint16_to_3i4(uint16_t packed) { glm::ivec3 res; // 解包X分量 const uint16_t x_sign = packed & 0x1U; const uint16_t x_abs = (packed >> 1) & 0xFU; res.x = static_cast<int>(x_abs) * (x_sign ? -1 : 1); // 解包Y分量 const uint16_t y_sign = (packed >> 5) & 0x1U; const uint16_t y_abs = (packed >> 6) & 0xFU; res.y = static_cast<int>(y_abs) * (y_sign ? -1 : 1); // 解包Z分量 const uint16_t z_sign = (packed >> 10) & 0x1U; const uint16_t z_abs = (packed >> 11) & 0xFU; res.z = static_cast<int>(z_abs) * (z_sign ? -1 : 1); return res; }
场景适配说明
如果你的原始坐标是浮点数类型,需要先完成量化再调用打包接口:
- 先获取网格整体的包围盒,计算每个轴的取值范围
- 将每个坐标归一化到[-1, 1]区间后乘以15,转成整数再传入打包函数即可
内容的提问来源于stack exchange,提问作者Samuel Wesley
相关产品推荐
相关产品推荐

