时间计算中DWORD减法回绕现象及运算结果可靠性咨询
问题背景
现有如下代码片段:
DWORD time1 = 0xFFFFFFFC; DWORD time2 = 0x0000000B; // 十进制11 DWORD time3 = 0x0000000D; // 十进制13 DWORD time4 = 0x0000000F; // 十进制15 swprintf_s(g_msgbuf, L"delta1: %i, %u\n", time2 - time1, time2 - time1); OutputDebugString(g_msgbuf); swprintf_s(g_msgbuf, L"delta2: %i, %u\n", time3 - time1, time3 - time1); OutputDebugString(g_msgbuf); swprintf_s(g_msgbuf, L"delta3: %i, %u\n", time4 - time1, time4 - time1); OutputDebugString(g_msgbuf); if ((time2 - time1) == 15) OutputDebugString(L"equal\n"); else OutputDebugString(L"not equal\n");
运行代码得到输出:
delta1: 15, 15 delta2: 17, 17 delta3: 19, 19 equal
待解答问题:
- 解释输出中15、17、19三个数值的来源与计算逻辑
- 实现简易游戏循环时需要测量两帧delta时间,上述代码模拟
DWORD类型时间值回绕场景(明确使用timeGetTime函数,无需推荐其他时间函数):后获取的时间值小于先获取的值触发回绕时,理论上减法似乎会得到负数,但实际输出的15、17、19恰好是正确的帧间隔(偏差为1),该场景下是否可以依赖这个隐式行为,不需要额外处理时间回绕?
回答
1. 数值计算逻辑
DWORD是Windows定义的32位无符号整数类型,无符号整数的所有算术运算严格遵循*模232*规则,不存在负数结果,运算结果会自动对232取模,这是计算的核心前提。
三个值的具体计算过程:
- delta1(
time2 - time1):0x0000000B - 0xFFFFFFFC = 0x0000000F,对应十进制15。32位补码规则下0x0000000F本身就是正整数15,所以不管用%i(有符号整型格式)还是%u(无符号整型格式)打印,结果都是15。 - delta2(
time3 - time1):0x0000000D - 0xFFFFFFFC = 0x00000011,对应十进制17,两种格式符打印结果一致。 - delta3(
time4 - time1):0x0000000F - 0xFFFFFFFC = 0x00000013,对应十进制19,打印结果一致。
后续判断(time2 - time1) == 15时,编译器会将右侧的有符号字面量15转换为无符号类型做比较,0xF和15值相等,因此输出equal。
你提到的1个计数偏差是毫秒计数的固有特性:timeGetTime返回系统启动后的毫秒计数,从0xFFFFFFFC开始,经过1毫秒到0xFFFFFFFD、2毫秒到0xFFFFFFFE、3毫秒到0xFFFFFFFF、4毫秒回绕到0,到值为11的0x0000000B时,实际经过的毫秒数正好是15,不存在计算误差。
2. 是否需要额外处理回绕
完全可以直接依赖这个行为,不需要额外写回绕判断逻辑。注意这不是编译器自定义的隐式行为,是C/C++语言标准明确规定的无符号运算规则,不存在跨编译器、跨版本的兼容性问题。
- C/C++标准对无符号整数运算的定义非常明确:无符号类型无法表示负数,运算结果按位宽取模2N,溢出、回绕都是完全合法的明确定义行为,不属于未定义行为。只要`DWORD`是32位无符号类型,两次获取的时间间隔不超过232毫秒(约49.7天),直接用后一次的时间值减前一次的时间值,得到的结果就是正确的间隔毫秒数,无论中间有没有发生回绕。
- 对游戏帧间隔计算场景来说,两帧间隔通常在几毫秒到几十毫秒,哪怕卡帧最多也就几秒,远小于49.7天的阈值,完全满足使用条件,不需要额外加判断。
- 唯一需要注意的点:不要把减法得到的无符号结果强转为有符号类型再参与其他计算,全程保持无符号类型运算就不会出错。
内容的提问来源于stack exchange,提问作者darro911
相关产品推荐
相关产品推荐

