GettickCountでOver Flowのエラー

解決


JJ  2010-07-09 20:49:02  No: 38776

すいません。
GetTickCountでリセットされてしまう対策の関数を作成し、使用しています。
特に問題がないと思ったのですが、Over Flowのエラーが出てしまうことがあります。
出るときと出ないときがあるのでげんいんがつかめません…

関数は下記です。
function GetTime(STime: DWORD): DWORD;
Var
  Time1,Time2:DWORD;
begin
  Time2 := GetTickCount- STime;

 if GetTickCount< STime then
 begin
   Time1 := 4294967295  - STime; 
   Time2 := GetTickCount+ Time1;
 end;

 result := Time2;

end;

この関数を使用しているとある処理によっては
  「Time2 := GetTickCount- STime;」
の部分でOver Flowのエラーが出ます。

私なりに修正して

function GetTime(STime: DWORD): DWORD;
Var
  Time1,Time2,NowTime :DWORD;
begin
  NowTime  := GetTickCount;
  Time2    := NowTime - STime;

 if NowTime < STime then
 begin
   Time1 := 4294967295  - STime; 
   Time2 := NowTime + Time1;
 end;

 result := Time2;

end;

としたらエラーは出なくなりました。
ただ、原因がわからないため、ほんとうにこの修正でよいのか、疑問です。
ただエラーが出なくなったというだけなので。

なぜ起こってしまうのかわかるかたいらっしゃいますか?


KHE00221  2010-07-09 21:48:52  No: 38777

DWORDのような整数しか範囲が無い型で
小さい数から多き数を引くと
マイナスになるのでオーバーフローが発生しますよ?

オーバーフローのチェック外せば出ませんが


KHE00221  2010-07-09 21:49:23  No: 38778

多き->大きい


JJ  2010-07-10 00:13:00  No: 38779

KHE00221さん

回答ありがとうございます。
確かにDWORDはマイナスの値は範囲ではありませんが、
Time2    := NowTime - STime;
のTime2がマイナスとなってしまう場合Time2の値は0になるのではないでしょうか?

またそれが原因でオーバーフローが発生してしまう場合
修正後にも発生してしまいませんか?

よろしくお願いします。


tor  2010-07-10 05:28:16  No: 38780

> Time2がマイナスとなってしまう場合Time2の値は0になるのではないでしょうか?
0にはなりません。実行時エラーか、でなければDWORDにキャストした値(たとえば-1→4294967295)になります。

オーバーフローエラーが出るような状態だと、修正後のコードでも
実際にGetTickCountが巻き戻ったらエラーになりますね。
こんな風になっていないといけないのでは?
NowTime := GetTickCount;
if NowTime < STime then
begin
  Time1 := 4294967295  - STime; 
  Result := NowTime + Time1 + 1; // 4294967295msと0msの間は1ms
end
else
  Result := NowTime - STime;

最初のコードでエラーが出て2番目で消えるというのは説明が付けにくいですね。
(エラーが出たのが2つ目や3つ目のGetTickCountの場所なら、その間にカウント値が変わったからだという解釈もできそうですが)
コードを試している間に、オーバーフローチェックの設定を変えたという可能性はないでしょうか?


KHE00221  2010-07-10 11:37:52  No: 38781

>出るときと出ないときがあるのでげんいんがつかめません…

GetTickCount って 49.7日までのはずだから

49.7日ぐらいに1回 Time2 := GetTickCount- STime; で
オーバーフローするはずなんだけど

>としたらエラーは出なくなりました。

修正してから 49.7 日たってないんじゃないの?


JJ  2010-07-12 18:20:02  No: 38782

回答ありがとうございます。

確かにマイナスになるような状況ではDWORDに
  Time2    := NowTime - STime;
こうしてしまうのではなくtorさんの言っているように
比較後計算しなくてはいけませんね。

「+ 1; // 4294967295msと0msの間は1ms」
はどういった場合、必要でしょうか?


ぽぽん  2010-07-12 20:59:53  No: 38783

動作の対象のOSがWindows Vista以降でしたら
GetTickCount64の使用をお勧めします

GetTickCount64
http://msdn.microsoft.com/ja-jp/library/ms724411(v=VS.85).aspx
GetTickCount
http://msdn.microsoft.com/ja-jp/library/cc429827.aspx


tor  2010-07-12 22:12:31  No: 38784

> 「+ 1; // 4294967295msと0msの間は1ms」
> はどういった場合、必要でしょうか?

どういった場合というか、
やりたいことはSTime(前回の値)とGetTickCountとの間の経過時間を求めることですよね?
例えば前回の値が4294967295で今回の値が0だったら、その間に1ms経過しているはずです。
元のコードだとその折り返し分を計算に入れていないので、結果が0msになってしまいます。

ちなみにオーバーフローチェックが無効になっているのなら、
ややこしいことを考えずに Result := GetTickCount-STime; で用が足ります。
で、結局チェックは有効、無効どっちになっているのでしょうか?(新規プロジェクトではデフォルトでオフだったと思いますが)


JJ  2010-07-13 18:32:27  No: 38785

回答ありがとうございます。

「+1」の意味理解できました。

私のほうで回答を見て色々試してみました。
結局、チェックが有効であれば、エラーになってしまうし、
無効であればキャストした値になりますね。
torさんのいわれているとおりのことが確認できました。

今回はオーバーフローチェックをONしてエラーになっていたので、それがわかって勉強になりました。

先に計算を行い、その計算結果から値をいれるやり方で対応したいと思います。

ありがとうございました。


JJ  2010-07-13 19:07:24  No: 38786

解決です。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加