|
▼初心者男 さん:
おはようございます。
非常にわかりやすい記述です。
こういう記述を続けていけば、きっとVBAの上達は速いと思いますよ!!
さて、本題ですが、小数を扱う時は要注意という典型例です。
もうちょっと簡単にしましょう!!
Sub kurikaesi()
Dim ans As Double
Dim step As Double
Dim max As Double
Dim x As Double
step = 0.05
max = 3.05
ans = step + max
x = 0
Do
x = x + step
Loop Until x = ans
MsgBox x
End Sub
これでもループを抜け出せないことを確認してください。
つまり、変数 xとans 等しくならない ということです。
理由は、小数が10進では確定された数値でも二進数に直すと循環小数になってしまう
場合があるからです。
MS社でも工夫しているようですが、この誤差を埋めることは出来ていません。
改めて新規ブックの標準モジュールに
'================================================================
Option Explicit
Sub kurikaesi()
Dim ans As Double
Dim step As Double
Dim max As Double
Dim x As Double
Dim cnt As Long
Dim ans_hex As String
Dim x_hex As String
step = 0.05
max = 3.05
ans = step + max
x = 0
Do
x = x + step
cnt = cnt + 1
If cnt = 62 Then Exit Do
Loop Until x = ans
MsgBox "ans=" & ans & vbCrLf & _
"x=" & x
MsgBox "ans = x ----- " & (ans = x)
ans_hex = floating_point(ans, 1)
x_hex = floating_point(x, 1)
MsgBox "ansの内部データ " & ans_hex & vbCrLf & _
"xの内部データ " & x_hex
End Sub
'=====================================================================
Function floating_point(ByVal myvalue As Variant, ByVal typ As Long) As String
'指定された型の数値のメモリーイメージをHEXコードで出力する
'in ----myvalue----数値
' typ=0--single 1--double 2----currency
'out-----floating_Point ---メモリーイメージ(HEXコードで)
On Error Resume Next
Const typ_sin = 0
Const typ_dbl = 1
Const typ_cur = 2
Const flnm = "\binary.tmp"
Dim dbb(0 To 7) As Byte
Dim sbb(0 To 3) As Byte
Dim idx As Long
Dim mes As String
Dim dd As Double
Dim ss As Single
Dim cc As Currency
Dim fnum As Long
Dim wk As String
Select Case typ
Case typ_sin
ss = CSng(myvalue)
Case typ_dbl
dd = CDbl(myvalue)
Case typ_cur
cc = CCur(myvalue)
End Select
Kill ThisWorkbook.Path & flnm
On Error GoTo 0
fnum = FreeFile()
Open ThisWorkbook.Path & flnm For Random As #fnum Len = IIf(typ = typ_sin, UBound(sbb()) + 1, UBound(dbb()) + 1)
Select Case typ
Case typ_sin
Put #fnum, , ss
Get #fnum, 1, sbb()
Case typ_dbl
Put #fnum, , dd
Get #fnum, 1, dbb()
Case typ_cur
Put #fnum, , cc
Get #fnum, 1, dbb()
End Select
Close #fnum
Kill ThisWorkbook.Path & flnm
floating_point = ""
For idx = IIf(typ = typ_sin, UBound(sbb()), UBound(dbb())) To 0 Step -1
If typ = typ_sin Then
wk = Hex(sbb(idx))
If Len(wk) = 1 Then wk = "0" & wk
floating_point = floating_point & wk
Else
wk = Hex(dbb(idx))
If Len(wk) = 1 Then wk = "0" & wk
floating_point = floating_point & wk
End If
Next
Erase sbb(), dbb()
End Function
** 一度、ブックを保存した後、kurikaesi を実行してください。 **
計算では、ループを62回繰り返せば、
Xは3.1 ansの3.1と同じになる 事を想定していますよね?
よって、上記のコードは62回のループを数えて繰り返しを抜けています。
実行してx とansの結果を比べてください
見た目の表示は 確かに3.1で同じですが、
x=ans は、決してTrueになりません。
ansと xは、倍精度浮動小数点型(8バイトデータ)ですから、
その内部形式まで調べてみると・・・、
やっと、微妙な桁数で違いが見られます。
この違いが本来なら抜けるはずのループ処理を
無限ループにしています。
でどのようにするかは、何が目的のコードなのかをお聞きしなくては
なりませんが、一例として
'========================================
Sub kurikaesi3()
Dim ans As Currency
Dim step As Currency
Dim max As Currency
Dim x As Currency
step = 0.05
max = 3.05
ans = step + max
x = 0
Do
x = x + step
Loop Until x = ans
MsgBox x
End Sub
というように固定小数点型のデータを使うと小数第2位程度なら、
結果を出してくれます。
繰り返しますが、小数を扱う場合は、要注意ですよ!!
|
|