C++游戏循环中依赖隐式转换处理timeGetTime回绕是否安全
你当前的游戏循环实现代码如下:
// ... DWORD oldtime = 0, newtime = 0, delta = 0; //... while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message == WM_QUIT) { exit(0); } TranslateMessage(&msg); DispatchMessage(&msg); } delta = newtime - oldtime; while (delta >= MS_PER_UPDATE) { //update(); delta -= MS_PER_UPDATE; g_loopCnt++; } //render(); // ...
依赖无符号运算处理时间回绕是否安全合规?
完全安全,属于标准合规写法,不需要额外编写手动校验逻辑。
你之前的认知存在一个偏差:DWORD是Windows明确定义的32位无符号整数类型,newtime、oldtime、delta三个变量全部为无符号类型,newtime - oldtime从运算开始就是无符号整数计算,不存在“有符号数算出负值再隐式转换为无符号数”的过程。
C/C++标准明确规定:无符号整数遵循模2N算术规则,溢出、回绕都是完全定义明确的行为,不属于未定义行为。当时间跨过232毫秒边界回绕到0时,无符号减法的结果刚好等于两个时间点的真实间隔:比如回绕前oldtime取值为0xFFFFFF9C(即2^32-100,距离回绕点还有100ms),回绕后newtime取值为50,直接做无符号减法得到的结果就是150,和实际经过的时间完全一致。
这种写法已经在Windows平台的游戏、多媒体程序中稳定使用了二十多年,从Win95时代的DirectX SDK示例到现在的各类开源游戏引擎,固定步长循环的时间差计算基本都采用该写法,经过了足够长时间的生产环境验证,不存在合规性问题。
检测到newtime < oldtime时调换操作数算差的写法是否合理?
完全不合理,会直接导致程序逻辑崩溃。
仍然用上述回绕场景举例:真实时间间隔是150ms,按照你给出的判断逻辑,newtime(50) < oldtime(0xFFFFFF9C)成立,之后计算delta = oldtime - newtime得到的结果是2^32 - 150 = 4294967146,这个值远大于常规的MS_PER_UPDATE(一般为16ms,对应60帧),会让后续的更新循环执行数千万次,直接导致程序卡死无响应。
如果一定要手动写分支处理回绕,正确的计算逻辑应为:
delta = (newtime >= oldtime) ? (newtime - oldtime) : (0xFFFFFFFF - oldtime + newtime + 1);
但这个写法的运算结果和无符号直接做减法的结果完全一致,属于冗余代码,没有任何实际价值。
额外注意事项
- 不要将
timeGetTime的返回值强转为有符号类型(int、long)存储或计算,这种操作才会真的出现负值,导致时间差计算错误。 - 你当前的固定步长累积逻辑本身没有问题,不需要为49.7天的回绕场景做任何特殊修改,无符号模运算已经天然处理了该边界情况。
内容的提问来源于stack exchange,提问作者darro911

