ページ 11

よろしくお願いします

Posted: 2011年6月14日(火) 11:21
by first
スリープを使い、経過した時間を1/50秒ずつ表示し続けてください
という問題を解いていて自分なりに考えてみたのですが微妙にずれて表示されてしまいます。
どこがおかしいのか教えていただけませんか。

#include "stdio.h"
#include "windows.h"

int main(void)
{
int t;

while(1){

t = GetTickCount();

printf(" %d 時間 %d 分 %d 秒 %d ミリ秒",
(t / 360000) % 24, (t / 60000) % 60, (t / 1000) % 60, t % 1000);

printf("\n");

Sleep(20);
}

for(; ;);
return 0;
}

よろしくお願いします

Re: よろしくお願いします

Posted: 2011年6月14日(火) 12:37
by ookami
状況がいまいち分かりませんが、
↓ こんなページは参考になりますでしょうか?
http://www.emit.jp/prog/prog_t1.html

Re: よろしくお願いします

Posted: 2011年6月14日(火) 12:48
by first
すみません・・・。
学校のパソコンを使っているので、規制がかかっていてみることができませんでした。

詳しく説明すると20ミリ秒刻みで時間を表示したいのに、15ミリ秒ですとか、ばらばらな時間で表示されてしまいます。

おわかりいただけたでしょうか?

Re: よろしくお願いします

Posted: 2011年6月14日(火) 13:01
by non
>15ミリ秒ですとか、ばらばらな時間

20ミリ秒より小さくなることがあるのですか?

Re: よろしくお願いします

Posted: 2011年6月14日(火) 13:47
by bitter_fox
non さんが書きました:>15ミリ秒ですとか、ばらばらな時間

20ミリ秒より小さくなることがあるのですか?
実行してみたところ確かになりますね。

コード:

 22 時間 29 分 16 秒 324 ミリ秒
 22 時間 29 分 16 秒 340 ミリ秒 *
 22 時間 29 分 16 秒 355 ミリ秒
 22 時間 29 分 16 秒 387 ミリ秒
 22 時間 29 分 16 秒 402 ミリ秒 *
 22 時間 29 分 16 秒 418 ミリ秒 *
 22 時間 29 分 16 秒 449 ミリ秒
 22 時間 29 分 16 秒 465 ミリ秒 *
 22 時間 29 分 16 秒 480 ミリ秒 *
 22 時間 29 分 16 秒 496 ミリ秒 *
 22 時間 29 分 16 秒 527 ミリ秒
 22 時間 29 分 16 秒 543 ミリ秒 *
 22 時間 29 分 16 秒 558 ミリ秒 *
 22 時間 29 分 16 秒 589 ミリ秒
・
・
・
このことは、MSDNのSleepの説明でも次のように触れられています。(http://msdn.microsoft.com/en-us/library/ms686298.aspx)
MSDN さんが書きました: If dwMilliseconds is less than the resolution of the system clock, the thread may sleep for less than the specified length of time.
If dwMilliseconds is greater than one tick but less than two, the wait can be anywhere between one and two ticks, and so on.
もしdwMillisecondsをシステムクロックの分解能よりも小さい値を指定した場合は、指定されている時間を満たさない場合もあるでしょう。
もしdwMillisecondsが二つの分解の間にある場合は、その間のいずれかのタイミングで復帰するでしょう。
MSDN さんが書きました: 略. So if one tick is 1/64 second (15.625ms), 略.
略。もし1分解を1/64秒(15.625ミリ秒)とした場合は、略。
となっていますので、もしシステムクロックの分解能が1/64secondの場合だと最大で15ミリ秒前後のずれが発生してしまう可能性があるでしょう。

[hr][追記]
[イメージ]

コード:

+-----+
|Sleep|
+-----+---------------------------------------------------+
|          1         2          3         4           |(per 64s(MSDNの資料の場合))
|          |         |          |  ・     |           |(・は分解上の引数の位置)
|                                  <--------->            |(復帰のタイミング)
+---------------------------------------------------------+

Re: よろしくお願いします

Posted: 2011年6月14日(火) 17:09
by ISLe
無限ループに入る前に
timeBeginPeriod(1);
と書いてみてください。
タイマー精度を『1』ミリ秒にするおまじないです。
#無限ループなのでtimeEndPeriod書くとこない。

プログラムを変更しなくても、Windows Media Playerとか起動しておくだけでも精度が上がりますよ。

15ミリ秒の分解能だと、15ミリ秒ごとに15増えるわけなので、14ミリ秒目~30ミリ秒目の16ミリ秒でも30増えたことになってしまいます。
逆に15増えたばかりのときから数えると、15ミリ秒経過では20を超えてなくて、30ミリ秒経過で20を超えることになります。

Re: よろしくお願いします

Posted: 2011年6月15日(水) 00:00
by Dixq (管理人)
短いスパンで見るからずれるのであって、長い目で見ればどうでしょう?
例えば最初に時刻を記憶しておき、それを基準に「時刻-時刻」で後何ミリ秒待機すべきか計算してはどうでしょうか。
1ミリ秒の誤差が1000回あれば1秒の誤差になってしまいますが、
最初の時刻から計算すれば1000回計算しても、1回分の誤差で済むかと思います。

Re: よろしくお願いします

Posted: 2011年6月21日(火) 12:26
by first
ありがとうございました