• ベストアンサー

「timer」の不思議

以下のようなコードを書くと timer2 < timer1となります。 不思議です。 幾らパソコンが速いといえどもおかしいのでは。 救える方法はあるのでしょうか。 timer1 = timer timer2 = timer そもそも、この質問は、 1秒ごとに、経過時間(hh:mm:ssのように)を表示したいと思い、、 ロジックを考えていて発見しました。 こちら(時計表示)についても合わせて教えていただけると嬉しいです。 宜しくお願いします。

質問者が選んだベストアンサー

  • ベストアンサー
  • cj_mover
  • ベストアンサー率76% (292/381)
回答No.1

こんにちは。 > 以下のようなコードを書くと timer2 < timer1となります。 > 不思議です。 > 幾らパソコンが速いといえどもおかしいのでは。 > 救える方法はあるのでしょうか。 特定の環境で起こる現象、ですので一般的なものではありませんが、、、。 以前某掲示板で話題にしたことありますが、原因は 「浮動小数点数の誤差」というものです。 二進数を基本に演算するPCと、十進数で考えることに慣れた人間と、 うまく付きあう為には、この誤差のことをよく理解した上で、 日頃から適切な対処を心掛けておいた方がいいと思います。 解決策としては、  Dim timer1 As Single  Dim timer2 As Single のように適切なデータ型を定義しておいてから、  timer1 = Timer  timer2 = Timer  MsgBox timer2 - timer1 という扱い方をするか、CSng()関数などを使い、 Single型同士で比較演算するように記述することです。 人間の眼には同じ値の小数に見える場合でも、格納さるデータ型がSingle型かDouble型かによって、 PCが扱う数値は同じ値にはならないのです。 Variant型を介して比較演算すれば、型のキャストが働きますが、そのままでは、 Single型をDouble型に変換してからの演算になってしまう場合があり、そこに誤差が生じます。 Single型なのかDouble型なのか、違いをはっきり意識して演算させてあげればいい、ということです。 ただ、この問題については、滅多に再現できるものではありません。 大抵の場合、誤差が問題とならずに演算出来てしまうか、誤差が生じることに気が付かない、 ということもありますが、 特定の環境でしか再現できない現象です。 たまたま私は経験したことがある、というだけで、現在手元にある3台のPCでは、再現できません。 Excelのバージョン、SP、KB、OSのバージョン、などなど、 お使いの環境を書いてあれば、より専門的な回答を付けてくれる人もいるかも知れません。 経過時間を正確に得る為には一般的にAPIのGetTickCount関数などをよく使います。  Private Declare Function GetTickCount Lib "kernel32" () As Long  Sub Test()   MsgBox GetTickCount  End Sub ミリ秒単位(1秒 = 1000)で比較的精度が高いですし、整数値を返しますから、 今回のような問題は決して起こりません。 ただ、APIの場合は、正しい扱い方をきちんと勉強してから、挑戦した方がいいです。 小さなミスを庇ってくれるVBAの親切設計とは違いますから、 例えばデータ型を間違えたりとか、基本的なミスには結構厳しいです。 エラーの種類によってはPC環境にも影響する、と警告する方もいらっしゃいますから、 扱いは慎重に越したことはないです。 例えば入門書で学んだ人の多くが読み飛ばしてしまうようなページに書かれているような 基本的な事を押えて、誰も書いていないような冒険的な記述を控えれば、 そんなに難しく考えなくても大丈夫かな、とは思います。 でもまぁAPI使えるようになると、VBAの可能性は飛躍的に拡大する、というのもありますね。 > そもそも、この質問は、 > 1秒ごとに、経過時間(hh:mm:ssのように)を表示したいと思い、、 > ロジックを考えていて発見しました。 > こちら(時計表示)についても合わせて教えていただけると嬉しいです。 > 宜しくお願いします。 APIの、SetTimerとKillTimer、とかを使うことになるかな?と思います。  【 VBA API SetTimer KillTimer 】 などで検索すれば色々サンプルは閲覧できますので、 どんなものかまず試してみる所から始めると好いと思います。 APIの中では、若干難度高めです。 他、ルーズなタイミング(ルーズな秒単位)でもよければ、Application.OnTime メソッドなども調べておくといいでしょう。 今回の目的に適さなかったとしても、必要な場面は出てくるでしょうから、覚えておいて損はないと思います。 タイマーを動かす間、一切の操作を無効にして待機するだけで良ければ、Application.Wait メソッドもあります。 以上、参考まで。

nagahaha
質問者

お礼

早速有り難うございました。 良くではありませんが、何となく分かりました。 >「浮動小数点数の誤差」というものです。 ということですね。 浮動小数点数というのは馴染みがないので、 そうかと思うしかありませんが、了解です。 色々な点も教えていただき、勉強になりました。 教えていただいたポイントで当たってみます。 お世話になりました。

その他の回答 (1)

回答No.2

解説は先達の方にお任せするとして でも、ちょっぴり。 WindowsのTimer関数は少数第二位まで返すようです(当方では)。 で、CPUのクロックは3.3GHz(当方) Timer関数が何クロックで終わるのか分かりませんが CPUは一秒間に3300000000回働くので同タイムを返すのでしょう。 DoEventsを挟んで余計なことをさせればタマに違う値を返します。 でも、 >timer2 < timer1となります とはならなんだ。 Sub testTimerFnc() ’当方だと12秒弱かかりました Dim timer1 As Single, timer2 As Single, i As Integer For i = 1 To 10000 timer1 = Timer DoEvents timer2 = Timer 'Debug.Print timer1, timer2 Debug.Print i, timer1 < timer2, timer1 > timer2, timer1 = timer2, IIf(timer1 < timer2, "★", "") If timer1 > timer2 Then Stop Next MsgBox "10000回試したよ" End Sub Sub atTimer() 'タイマー中何もできない手抜きバージョン Dim t As Double, a As Double, i As Integer Range("A1").NumberFormatLocal = "h:mm:ss;@" a = Now Do t = Now Cells(1, 1) = CDate(t - a) Application.Wait Now + TimeValue("0:0:1") i = i + 1 If i > 30 Then Exit Do 'テキトーなところで終了 Loop End Sub お邪魔様でした。

nagahaha
質問者

お礼

早速有り難うございました。 例示いただいたプロックをやってみましたが、 逆転しませんでした。 やはりテクニックがあったのですね、 無知なモノで。 今後は大丈夫かと思います。 お世話になりました。

関連するQ&A

専門家に質問してみよう