Excel VBA質問箱 IV

当質問箱は、有志のボランティア精神のおかげで成り立っています。
問題が解決したら、必ずお礼をしましょうね。
本サイトの基本方針をまとめました。こちら をご一読ください。

投稿種別の選択が必要です。ご注意ください。
迷惑投稿防止のため、URLの入力を制限しています。ご了承ください。


4913 / 13646 ツリー ←次へ | 前へ→

【53662】Worksheetfunction.Floor について 映画館スタッフ 08/1/29(火) 22:53 質問[未読]
【53663】Re:Worksheetfunction.Floor について ichinose 08/1/29(火) 23:47 発言[未読]
【53664】Re:Worksheetfunction.Floor について 映画館スタッフ 08/1/30(水) 9:42 お礼[未読]

【53662】Worksheetfunction.Floor について
質問  映画館スタッフ  - 08/1/29(火) 22:53 -

引用なし
パスワード
   こんばんわ 早速ですが質問させていただきます。

シートAに5分単位での時間表が作成されています。(9:00、9:05、9:10・・・)
シートBにはランダムでの時間データが入っています(9:46、11:57・・・)

シートBにあるデータをそれぞれ5分単位で切り下げた時間にして、シートAの該当する部分へ書き込みたいのです。
9:46なら9:45の部分へ、23:04なら23:00へ。
そこで Worksheetfunction.Floor にて5分単位への切り下げを行って時間の比較を行っているのですが、これが正しく反映されません。
試しに下のコードでやってみたところ、同じ23:00同士なのに不一致とみなされてしまいます。

Sub main()

  Dim time1 As Date, time2 As Date
  
  time1 = CDate("23:01")
  time2 = CDate("23:00")
  
  'time1 を5分単位に切り下げ
  time1 = WorksheetFunction.Floor(time1, CDate("0:05"))
  
  If time1 = time2 Then
    Debug.Print "ok"
  Else
    Debug.Print "no"
  End If
  
End Sub

ローカルウインドウで見ても、データはどちらも23:00なのですが・・・
色々試したところ、上のコードで言うと time2 を time1 と同じように Floor関数にて5分単位で切り下げると、 time1 = time2 の式が真と判断されます。
ですが元々5分単位で作成してあるデータを5分単位で切り下げるというのが納得いかないのです。
Floor関数によって切り下げたデータは何か根本的に変わってしまうのでしょうか?

【53663】Re:Worksheetfunction.Floor について
発言  ichinose  - 08/1/29(火) 23:47 -

引用なし
パスワード
   こんばんは。

時間を表す Cdate("23:01") や Cdate("0:05") って

sub test()
  MsgBox CDbl(CDate("23:01"))
  MsgBox CDbl(CDate("0:05"))
end sub
なんてコードで確認するとわかりますが、小数点以下の桁数が大きい小数ですよね!!

PCはこういう計算が弱いんです。
理由は、

www.vbalab.net/vbaqa/c-board.cgi?cmd=ntr;tree=46479;id=excel

↑ここと同じ理由です。

WorksheetFunction.Floor(time1, CDate("0:05"))

この関数って実際には

Int(time1 / CDate("0:05")) * CDate("0:05")

↑こんな計算を行ってくれる関数です。

小数点以下の桁数が大きい小数同士で上記のような計算を行うと
誤差はでる可能性は大きいです。

時刻を秒単位にする、つまり、整数で比較すると正しい比較が行えます

'=================================================================
Sub main()

  Dim time1 As Double, time2 As Double
 
  time1 = CDate("23:01") * 24 * 60 * 60
  time2 = CDate("23:00") * 24 * 60 * 60
 
  'time1 を5分単位に切り下げ
  time1 = WorksheetFunction.Floor(time1, CDate("0:05") * 24 * 60 * 60)
 
  If time1 / 86400 = time2 / 86400 Then
    Debug.Print "ok"
  Else
    Debug.Print "no"
  End If
  MsgBox Format(time1 / 86400, "hh:mm:ss")
   
End Sub


プログラミングでは、小数の計算は 要注意ですよ!!

【53664】Re:Worksheetfunction.Floor について
お礼  映画館スタッフ  - 08/1/30(水) 9:42 -

引用なし
パスワード
   ▼ichinose さん:
ありがとうございます。
小数点以下の演算が問題点だったのですね
Floor関数の内部構造なども、勉強になりました!

精度を上げて秒数まで整数にして計算するか、Floor関数を比較対照のデータにも行うか、Format(time1,"h:mm") = Format(time2,"h:mm") で時刻表示にして比較するか、色々やってみようと思います。
お世話になりました。

4913 / 13646 ツリー ←次へ | 前へ→
ページ:  ┃  記事番号:
2610219
(SS)C-BOARD v3.8 is Free