アプリ版:「スタンプのみでお礼する」機能のリリースについて

以下のようなコードを書くと timer2 < timer1となります。
不思議です。
幾らパソコンが速いといえどもおかしいのでは。
救える方法はあるのでしょうか。

timer1 = timer
timer2 = timer

そもそも、この質問は、
1秒ごとに、経過時間(hh:mm:ssのように)を表示したいと思い、、
ロジックを考えていて発見しました。

こちら(時計表示)についても合わせて教えていただけると嬉しいです。
宜しくお願いします。

A 回答 (2件)

こんにちは。



> 以下のようなコードを書くと 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 メソッドもあります。

以上、参考まで。
    • good
    • 0
この回答へのお礼

早速有り難うございました。
良くではありませんが、何となく分かりました。
>「浮動小数点数の誤差」というものです。
ということですね。

浮動小数点数というのは馴染みがないので、
そうかと思うしかありませんが、了解です。

色々な点も教えていただき、勉強になりました。
教えていただいたポイントで当たってみます。
お世話になりました。

お礼日時:2014/07/19 21:05

解説は先達の方にお任せするとして


でも、ちょっぴり。
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

お邪魔様でした。
    • good
    • 0
この回答へのお礼

早速有り難うございました。
例示いただいたプロックをやってみましたが、
逆転しませんでした。

やはりテクニックがあったのですね、
無知なモノで。

今後は大丈夫かと思います。
お世話になりました。

お礼日時:2014/07/19 21:07

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!