Delphi:如何避免EIntOverflow下溢? [英] Delphi: How to avoid EIntOverflow underflow when subtracting?
问题描述
不正确(伪代码):
code> DWORD endTime = GetTickCount + 10000; // 10 s from now
...
if(GetTickCount> endTime)
break;
上面的代码是坏的,因为它可以接受点击计数器的翻转。例如,假设时钟接近结束范围:
endTime = 0xfffffe00 + 10000
= 0x00002510 ; // 9,488 decimal
然后执行支票:
if(GetTickCount> endTime)
其中因为 GetTickCount
c $ c> endTime :
if(0xfffffe01> 0x00002510)
解决方案
相反,您应该总是减去两个时间间隔:
DWORD startTime = GetTickCount;
...
if(GetTickCount - startTime)> 10000 //如果是10秒
break;
查看相同的数学:
if(GetTickCount - startTime)> 10000
if(0xfffffe01 - 0xfffffe00)> 10000
如果(1> 10000)
很好的C / C ++,编译器以某种方式行事。
但是Delphi呢?
但是当我在Delphi中执行相同的数学计算时,会进行溢出检查( {Q +}
, {$ OVERFLOWCHECKS ON}
)当 TickCount 滚动时,减去两个滴答计数会产生EIntOverflow异常:
if (0x00000100 - 0xffffff00)> 10000
0x00000100 - 0xffffff00 = 0x00000200
这是什么意图解决方案问题?
编辑:我试图暂时关闭 OVERFLOWCHECKS
:
{$ OVERFLOWCHECKS OFF}]
delta = GetTickCount - startTime;
{$ OVERFLOWCHECKS ON}
但减法仍然会抛出一个 EIntOverflow
异常
有没有更好的解决方案,涉及到转换和更大的中间变量类型?
更新
另一个我问的问题解释了为什么 {$ OVERFLOWCHECKS} code>不起作用它显然只适用于功能级别,而不是行级别。所以,以下不工作:
{$ OVERFLOWCHECKS OFF}]
delta = GetTickCount - startTime;
{$ OVERFLOWCHECKS ON}
以下 工作:
delta:= Subtract(GetTickCount,startTime);
{$ OVERFLOWCHECKS OFF}]
函数Subtract(const B,A:DWORD):DWORD;
begin
结果:=(B - A);
结束
{$ OVERFLOWCHECKS ON}
一个这样一个简单的函数?
function GetElapsedTime(LastTick:Cardinal):Cardinal;
var CurrentTick:Cardinal;
begin
CurrentTick:= GetTickCount;
如果CurrentTick> = LastTick然后
结果:= CurrentTick - LastTick
else
结果:=(高(Cardinal) - LastTick)+ CurrentTick;
结束
所以你有
StartTime:= GetTickCount
...
如果GetElapsedTime(StartTime)> 10000然后
...
只要StartTime和当前的GetTickCount小于GetTickCount臭名昭着的49.7天范围。
Microsoft already says, in the documentation for GetTickCount, that you could never compare tick counts to check if an interval has passed. e.g.:
Incorrect (pseudo-code):
DWORD endTime = GetTickCount + 10000; //10 s from now
...
if (GetTickCount > endTime)
break;
The above code is bad because it is suceptable to rollover of the tick counter. For example, assume that the clock is near the end of it's range:
endTime = 0xfffffe00 + 10000
= 0x00002510; //9,488 decimal
Then you perform your check:
if (GetTickCount > endTime)
Which is satisfied immediatly, since GetTickCount
is larger than endTime
:
if (0xfffffe01 > 0x00002510)
The solution
Instead you should always subtract the two time intervals:
DWORD startTime = GetTickCount;
...
if (GetTickCount - startTime) > 10000 //if it's been 10 seconds
break;
Looking at the same math:
if (GetTickCount - startTime) > 10000
if (0xfffffe01 - 0xfffffe00) > 10000
if (1 > 10000)
Which is all well and good in C/C++, where the compiler behaves a certain way.
But what about Delphi?
But when i perform the same math in Delphi, with overflow checking on ({Q+}
, {$OVERFLOWCHECKS ON}
), the subtraction of the two tick counts generates an EIntOverflow exception when the TickCount rolls over:
if (0x00000100 - 0xffffff00) > 10000
0x00000100 - 0xffffff00 = 0x00000200
What is the intended solution for this problem?
Edit: i've tried to temporarily turn off OVERFLOWCHECKS
:
{$OVERFLOWCHECKS OFF}]
delta = GetTickCount - startTime;
{$OVERFLOWCHECKS ON}
But the subtraction still throws an EIntOverflow
exception.
Is there a better solution, involving casts and larger intermediate variable types?
Update
Another SO question i asked explained why {$OVERFLOWCHECKS}
doesn't work. It apparently only works at the function level, not the line level. So while the following doesn't work:
{$OVERFLOWCHECKS OFF}]
delta = GetTickCount - startTime;
{$OVERFLOWCHECKS ON}
the following does work:
delta := Subtract(GetTickCount, startTime);
{$OVERFLOWCHECKS OFF}]
function Subtract(const B, A: DWORD): DWORD;
begin
Result := (B - A);
end;
{$OVERFLOWCHECKS ON}
How about a simple function like this one?
function GetElapsedTime(LastTick : Cardinal) : Cardinal;
var CurrentTick : Cardinal;
begin
CurrentTick := GetTickCount;
if CurrentTick >= LastTick then
Result := CurrentTick - LastTick
else
Result := (High(Cardinal) - LastTick) + CurrentTick;
end;
So you have
StartTime := GetTickCount
...
if GetElapsedTime(StartTime) > 10000 then
...
It will work as long as the time between StartTime and the current GetTickCount is less than the infamous 49.7 days range of GetTickCount.
这篇关于Delphi:如何避免EIntOverflow下溢?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!