NAN Box负整数识别失效:INT_MASK的正确取值及实现疑问
关于NaN Boxing实现中整数类型识别问题的疑问
我参考了一篇讲解NaN Boxing的文章,并尝试在自己的语言中实现该机制。以下是我的实现代码:
typedef union { uint64_t as_uint; double as_double; } Atom; #define NANISH 0x7ffc000000000000 /* 用额外一位区分"我们的"NaN */ #define NANISH_MASK 0xffff000000000000 /* [符号/指针标签] + 11位[指数] + 2位[NANISH] + 2位[标签] */ #define BOOL_MASK 0x7ffe000000000002 /* 2个最高位 + 2个最低位 */ #define NULL_VALUE 0x7ffe000000000000 /* 0b*00 */ #define TRUE_VALUE (BOOL_MASK | 3) /* 0b*11 */ #define FALSE_VALUE (BOOL_MASK | 2) /* 0b*10 */ #define INT_MASK 0x7ffc000000000000 /* 用所有尾数位存储整数 */ #define SYM_MASK 0xfffc000000000000 /* 指针符号位已设置 */ #define STR_MASK 0xfffe000000000000 /* x86-64下指针最长48位 */ #define OBJ_MASK 0xfffd000000000000 /* 长度足够放入尾数 */ #define PTR_MASK 0xf000000000000000 /* 类型判断宏 */ #define DOUBLP(v) ((v.as_uint & NANISH) != NANISH) #define NULLP(v) ((v.as_uint == NULL_VALUE)) #define BOOLP(v) ((v.as_uint & BOOL_MASK) == BOOL_MASK) #define PTRP(v) ((v.as_uint & PTR_MASK) == PTR_MASK) #define INTP(v) ((v.as_uint & NANISH_MASK) == INT_MASK) #define STRP(v) ((v.as_uint & NANISH_MASK) == STR_MASK) #define SYMP(v) ((v.as_uint & NANISH_MASK) == SYM_MASK) #define OBJP(v) ((v.as_uint & NANISH_MASK) == OBJ_MASK) /* 获取值的宏 */ #define AS_DOUBL(v) (v.as_double) #define AS_BOOL(v) ((char)(v.as_uint & 0x1)) #define AS_INT(v) ((int32_t)(v.as_uint)) #define AS_PTR(v) ((char *)((v).as_uint & 0xFFFFFFFFFFFF)) /* 添加标签掩码的宏 */ #define TO_VEC(p) ((uint64_t)(p) | VEC_MASK) #define TO_STR(p) ((uint64_t)(p) | STR_MASK) #define TO_SYM(p) ((uint64_t)(p) | SYM_MASK) #define TO_MAP(p) ((uint64_t)(p) | MAP_MASK) #define TO_SET(p) ((uint64_t)(p) | SET_MASK) #define TO_INT(i) ((uint64_t)(i) | INT_MASK)
我还添加了一些自定义对象,但核心逻辑与原文章一致。测试代码如下:
int main() { Atom atom; atom.as_uint = TO_INT(-3); printf("%d\n", AS_INT(atom)); printf("%d\n", INTP(atom)); printf("%x\n", AS_INT(atom)); }
测试输出为:
-3 0 fffffffd
据我理解,负整数以补码形式存储,导致其位表示无法匹配INT_MASK,使得INTP宏返回0(无法识别为整数类型)。我曾考虑将INT_MASK修改为0xfffff,但这会与无符号整数及其他掩码产生冲突。请问我是否误解了文章内容?INT_MASK的正确取值应为多少?
解答
问题出在负整数的位扩展和INT_MASK的匹配逻辑上:
- 执行
TO_INT(-3)时,(uint64_t)(i)会把int32_t的-3(补码为0xfffffffd)扩展为uint64_t的0xfffffffffffffffd,再和INT_MASK(0x7ffc000000000000)做或运算,最终得到的as_uint值是0xfffffffffffffffd。 - 此时用
INTP(v)判断时,v.as_uint & NANISH_MASK(0xffff000000000000)的结果是0xffff000000000000,与INT_MASK(0x7ffc000000000000)不相等,所以返回0。
正确的整数标签设计思路
在NaN Boxing中,整数类型需要占用固定的高位标签位,同时低位存储整数的补码。你需要确保:
- 整数的高位标签位不会被负整数的符号扩展覆盖
- 类型判断掩码能准确匹配标签位
修正方案:
调整
TO_INT宏的实现:
确保只保留整数的低48位(x86-64下足够存储指针/整数),再和INT_MASK做或运算,避免符号扩展污染高位标签:#define TO_INT(i) ((uint64_t)(i) & 0xFFFFFFFFFFFF) | INT_MASK这样负整数的符号位只会扩展到低48位,不会影响高16位的标签位。
修正代码笔误:
OBJP宏中误写为BOJ_MASK,应改为OBJ_MASK,否则无法正确识别对象类型。
验证修正后的逻辑
执行TO_INT(-3)时:
(uint64_t)(-3) & 0xFFFFFFFFFFFF得到0x0000fffffffffffffd- 和
INT_MASK(0x7ffc000000000000)或运算后得到0x7ffcfffffffffffffd INTP(v)判断时,v.as_uint & NANISH_MASK结果为0x7ffc000000000000,与INT_MASK相等,返回1,正确识别为整数类型。
如果只需要32位整数,也可以改为保留低32位,不影响核心逻辑:
#define TO_INT(i) ((uint64_t)(i) & 0xFFFFFFFF) | INT_MASK
内容的提问来源于stack exchange,提问作者yan3ku
相关产品推荐
相关产品推荐

