Sample Source


【Source】 備考
コンボボックス VB6 sp4
リストビュアー VB6 sp4
エクセルへ出力 VB6 sp4
プログレスバー VB6 sp4
 
カーソル
Screen.MousePointer = vbDefault ' マウス ポインタを元に戻します。
定数 設定値 内容 
vbDefault 0 (既定値) 形状はオブジェクトによって決定されます。 
VbArrow 1 矢印。 
VbCrosshair 2 クロス。十文字のポインタです。 
VbIbeam 3 I - ビーム。アルファベットの I の形のポインタです。 
VbIconPointer 4 アイコン。2 重の四角形のポインタです。 
VbSizePointer 5 サイズ (上下左右)。上下左右を指している矢印のポインタです。 
VbSizeNESW 6 サイズ (右上 - 左下)。斜めに 2 方向を指している矢印のポインタです。 
VbSizeNS 7 サイズ (上下)。上下 2 方向を指している矢印のポインタです。 
VbSizeNWSE 8 サイズ (左上 - 右下)。斜めに 2 方向を指している矢印のポインタです。 
VbSizeWE 9 サイズ (左右)。左右 2 方向を指している矢印のポインタです。 
VbUpArrow 10 上矢印。 
VbHourglass 11 砂時計 (待機)。 
VbNoDrop 12 禁止。 
VbArrowHourglass 13 矢印と砂時計。 
VbArrowQuestion 14 矢印と疑問符。 
VbSizeAll 15 サイズ (すべて)。 
VbCustom 99 MouseIcon プロパティによって設定するユーザー定義のアイコンです。 
 
ファイルサーチ
Option Explicit
' ファイルを検索する。
' RootPath : 検索を開始する基準のディレクトリ
' InputPathName : 検索するファイル名
' OutputPathBuffer : 見つかったファイル名を格納するバッファ。
' 戻り値 : 見つかると0以外を返す。
Private Declare Function SearchTreeForFile Lib "imagehlp.dll" _
(ByVal RootPath As String, _
ByVal InputPathName As String, _
ByVal OutputPathBuffer As String) As Long
Private Const MAX_PATH = 512
Private Const MAX_PATH_PLUS1 = MAX_PATH + 1
Private Sub Command1_Click()
Dim lngResult As Long
Dim strBuffer As String * MAX_PATH_PLUS1

lngResult = SearchTreeForFile("C:\", "ak09.jpg", strBuffer)
If (lngResult <> 0) Then
Debug.Print Left$(strBuffer, InStr(strBuffer, vbNullChar) - 1)
End If
End Sub
 
カレントパス
App.path
 
VB アプリケーションのインスタンスが複数起動されるのを防止
If App.PrevInstance Then
MsgBox "このアプリケーションはすでに開かれています。" _
, vbExclamation, "ロードエラー"
Unload Me
End If
 
関連付けられている実行可能ファイルによりファイルを開く
Private Declare Function ShellExecute Lib "shell32.dll" Alias "ShellExecuteA" _ 
(ByVal hwnd As Long, ByVal lpOperation As String, ByVal lpFile As String, ByVal lpParameters As String, _ 
ByVal lpDirectory As String, ByVal nShowCmd As Long) As Long

Public Function ShellEx(FileName As String) As Boolean

Const SW_SHOWNORMAL = 1
Dim Ret As Long

'関連付けられている実行可能ファイルによりファイルを開く
Ret = ShellExecute(0, "open", FileName, vbNullString, Environ("windir"), SW_SHOWNORMAL)

ShellEx = Ret > 32

End Function
 
テキストボックスのポップアップメニューをカスタマイズする
Form1 に、メニューエディタで Menu1 というメニューを作成します。
これをコンテキストメニューにするには、以下のコードを Textbox の
MouseDown イベントに記述してください。

If Button = vbRightButton Then
Text1.Enabled = False
Text1.Enabled = True
Text1.SetFocus
PopUpMenu Menu1
End If 
 
最小化されたフォームからのユーザーへの通知
Private Sub Timer1_Timer()
Static ilmage As Integer
ilmage = ilmage + 1
If ilmage > 3 Then ilmage = 1
Me.Icon = ImageList1.ListImages(ilmage).Picture
EndSub
 
フォーカス取得時にコントロールのテキストを選択する
Private Sub Text1_GotFocus()
Screen.ActiveControl.SelStart = 0
Screen.ActiveControl.SelLength = Len(Screen.ActiveControl.Text)
End Sub

次のような方法を利用すると、text プロパティを持つ全ての
コントロールで使えるようになります。
Public Sub SelAllText(ctl As Control)
ctl.SelStart = 0
ctl.SelLength = Len(ctl.Text)
End Sub
 
Win32 アプリケーションを起動させ、終了させる
Visual Basic アプリケーションから 別の Win32 アプリケーションを起動する方法は、
次の 2 通りの方法があります。
  • Visual Basic の Shell コマンドを使用する方法。この方法を実行すると、
    新規プロセスを生成し、生成されたプロセスのプロセス ID を返します。
    このときプロセス ハンドルを取得する必要があります。プロセス ハンドルは、
     OpenProcess API 呼び出しで取得することができます。
  • プロセス オブジェクトとメイン スレッド オブジェクトを同時に生成する
    CreateProcess API 関数を使用する方法。プロセスとイニシャル スレッドは、
    生成時に 32 ビットの識別子 (ID) が割り当てられます。この ID は、
    それぞれのオブジェクト (プロセスまたはスレッド) が破棄されるまで有効です。
    この 32 ビット ID は、システム内でオブジェクトを一意に識別するために使われます。
    新しく生成されたプロセスとスレッドには、フル アクセス権を持つハンドルが
    それぞれに生成されます。これら 4 つの値は、CreateProcess に渡される
    ROCESS_INFORMATION 構造体に返されます。
どちらの場合もプロセス ハンドルというものが返されていますが、このプロセス ハンドルは、
新規に起動したアプリケーションを他の Win32 API (TerminateProcess など) から
操作するために使用します。

システムはオブジェクト生成時、各オブジェクトに使用カウントの初期値として 1 を
割り当てます。次に CreateProcess は戻る直前に、プロセス オブジェクトと
スレッド オブジェクトの両方をオープンし、PROCESS_INFORMATION 構造体の
hProcess と hThread メンバそれぞれに関連したハンドルを格納します。

CreateProcess がこれらのオブジェクトをオープンすると、各オブジェクトの
使用カウントはそれぞれ 2 にインクリメントされます。逆にプロセス終了時は、
まずプロセスを終了し (この時点で使用カウントは 1 になります。)、次に親プロセスから
CloseHandle (この時点で使用カウントは 0 になります。) を呼び出します。
これらの手順が終了した時点で Windows NT エグゼクティブはそのプロセス オブジェクトを
開放することができます。スレッド オブジェクトを開放するには、スレッドを終了し、
次にそのスレッド オブジェクトを示すハンドルを親プロセスがクローズしなければいけません。
  重要: ハンドルのクローズは大変重要です。ハンドルのクローズがきちんと行われて
        いないと、Windows NT エグゼクティブ オブジェクトの一部が破棄されないま
        ま残ってしまうため、メモリ リークを引き起こす可能性があります。
OpenProcess でプロセス ハンドルを取得する場合も、同じ注意が必要です。
この場合も同様にして、使用カウントが一つインクリメントされます。
そしてプロセス自体が終了してもハンドルがクローズされるまで、
プロセス オブジェクトはメモリに常駐しつづけます。

手順

  1. Visual Basic で新規プロジェクトを作成します。デフォルトでは Form1 が作成されます。
    フォームにコマンド ボタンを2個配置します。

     

  2. Form1 の General Declarations セクションに次のコードを追加します。
      Option Explicit
      Private Type PROCESS_INFORMATION
         hProcess    As Long
         hThread     As Long
         dwProcessId As Long
         dwThreadId  As Long
      End Type
      Private Type STARTUPINFO
         cb              As Long
         lpReserved      As String
         lpDesktop       As String
         lpTitle         As String
         dwX             As Long
         dwY             As Long
         dwXSize         As Long
         dwYSize         As Long
         dwXCountChars   As Long
         dwYCountChars   As Long
         dwFillAttribute As Long
         dwFlags         As Long
         wShowWindow     As Integer
         cbReserved2     As Integer
         lpReserved2     As Long
         hStdInput       As Long
         hStdOutput      As Long
         hStdError       As Long
      End Type
    
      Private Declare Function CreateProcess Lib "kernel32" _
        Alias "CreateProcessA" ( _
        ByVal lpApplicationName As String, _
        ByVal lpCommandLine As String, _
        lpProcessAttributes As Any, _
        lpThreadAttributes As Any, _
        ByVal bInheritHandles As Long, _
        ByVal dwCreationFlags As Long, _
        lpEnvironment As Any, _
        ByVal lpCurrentDriectory As String, _
        lpStartupInfo As STARTUPINFO, _
        lpProcessInformation As PROCESS_INFORMATION _
        ) As Long
    
      Private Declare Function OpenProcess Lib "kernel32.dll" ( _
        ByVal dwAccess As Long, _
        ByVal fInherit As Integer, _
        ByVal hObject As Long _
        ) As Long
    
      Private Declare Function TerminateProcess Lib "kernel32" ( _
        ByVal hProcess As Long, _
        ByVal uExitCode As Long _
        ) As Long
    
      Private Declare Function CloseHandle Lib "kernel32" ( _
        ByVal hObject As Long _
        ) As Long
    
      Const SYNCHRONIZE = 1048576
      Const NORMAL_PRIORITY_CLASS = &H20&
  3. Command1_Click イベントに次のコードを追加します。
      Dim pInfo As PROCESS_INFORMATION
      Dim sInfo As STARTUPINFO
      Dim ret   As Long
    
      sInfo.cb = LenB(sInfo)
      ret = CreateProcess(vbNullString, _
                         "calc.exe", _
                         ByVal 0&, _
                         ByVal 0&, _
                         1&, _
                         NORMAL_PRIORITY_CLASS, _
                         ByVal 0&, _
                         vbNullString, _
                         sInfo, _
                         pInfo _
      )
    
      MsgBox "[電卓] を終了させます"
    
      TerminateProcess pInfo.hProcess, 0&
      CloseHandle pInfo.hThread
      CloseHandle pInfo.hProcess
  4. Command2_Click イベントに次のコードを追加します。
      Dim ProcessID     As Long
      Dim ProcessHandle As Long
      ProcessID = Shell("calc.exe", vbNormalFocus)
      ProcessHandle = OpenProcess(SYNCHRONIZE, True, ProcessID)
    
      MsgBox "[電卓] を終了させます"
    
      TerminateProcess ProcessHandle, 0&
      CloseHandle ProcessHandle
  5. Ctrl + F5 キーを押してプログラムを実行します。Command1 をクリックします。
    メッセージと同時に Windows の [電卓] が起動します。
    メッセージ ボックスの [OK] をクリックすると [電卓] が終了します。
    Shell コマンドと OpenProcess を使った場合の動作を確認したい場合は、
    Command2 クリックします。
 
プロセスの終了を判断1(BF)
Option Explicit
  Private Declare Function WaitForSingleObject Lib "kernel32" _
      (ByVal hHandle As Long, ByVal dwMilliseconds As Long) As Long
  Private Declare Function OpenProcess Lib "kernel32" _
      (ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, _
      ByVal dwProcessId As Long) As Long
  Private Const INFINITE = -1&

Private Sub Command1_Click()
  Call Wait_ProcessEnd("C:\WINDOWS\CALC.EXE")
End Sub

Private Sub Wait_ProcessEnd(sCmdLine As String)
  Dim lRet As Long
  Dim lProcessID As Long
  Dim hHandle As Long

  lProcessID = Shell(sCmdLine, 1)
  hHandle = OpenProcess(&H100000, True, lProcessID)
  lRet = WaitForSingleObject(hHandle, INFINITE)
End Sub
 
プロセスの終了を判断2
Option Explicit
  Private Declare Function OpenProcess Lib "kernel32" _
   (ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, ByVal dwProcessId As Long) As Long
  Private Declare Function GetExitCodeProcess Lib "kernel32" _
   (ByVal hProcess As Long, lpExitCode As Long) As Long

Sub Process_rtn()
 Dim lonInstance As Long, lonProcess As Long, lonRet As Long, koncd As Long
 lonInstance = Shell("C:\WINDOWS\CALC.EXE", 1)
 lonProcess = OpenProcess(&H400 Or &H100000, True, lonInstance)
 Do
  lonRet = GetExitCodeProcess(lonProcess, koncd)
  DoEvents
 Loop Until koncd <> &H103&
End Sub
 
丸めを行うカスタム プロシージャを実装する方法
概要
マイクロソフト製品で使用可能な "丸め" アルゴリズムは複数存在します。丸めのアルゴリズムには、
Excel の Round() ワークシート関数における "算術型" 丸め (以下 "四捨五入") から、
Visual Basic for Applications (以下 VBA) の CInt()、CLng()、
および Round() 関数における "銀行型" 丸め まで、さまざまな方式があります。
この資料では、VBA のさまざまな丸めを行う関数の動作、および関数の使用方法について説明します。
さらに、さまざまな丸めのアルゴリズムを実装するサンプル関数も紹介します。 
詳細
丸めの概要
精度の高い数を精度の低い数に変換するときには、数を丸める必要があります。
最も一般的な例としては、浮動小数点数を整数に変換する必要がある場合です。

切り捨て
丸めの最も単純な方式は切り捨てです。目的の桁以降の数は、
すべて単に無視されます。切り捨ての例として、VBA の Fix() 関数が挙げられます。
たとえば、Fix(3.5) は 3、Fix(-3.5) は -3 になります。

Int() 関数を使用すると、引数の値を超えない最大の整数が返されます
。正の数については、Int() と Fix() の両方で同じように切り捨てが行われますが、
負の数の場合は異なります。たとえば、Int(-3.5) の結果は -4 になります。

Fix() 関数は、正の数と負の数の絶対値 (大きさ) を同じ方法で切り捨てるため、
対称的な丸めの例として挙げることができます。Int() 関数は、
切り捨てた後の絶対値が正の数と負の数で異なるため、非対称的な丸めの一例です。

Excel には、類似したワークシート関数 Int()、Floor()、および RoundDown() があります。
Int() は、VBA の Int() と同じ動作をします。Floor() は正の数に対しては切り捨てを行いますが、
負の数に対しては行いません。RoundDown() 関数は、VBA の Fix() 関数と同じ動作をします。

Microsoft SQL Server には、VBA の Fix() 関数と同様の動作をする Round() 関数があります。
また、VBA の Int() 関数と同じような動作をする Floor() 関数もあります。

切り上げ
SQL Server と Excel の両方には Ceiling() と呼ばれる関数があり
、端数値を最も近い値に (正の側に) 切り上げます。

VBA には、これに対応する切り上げ関数がありませんが、負の数に関しては、
Fix() と Int() を使用して切り上げることができます (切り上げの方式は異なります)。

Fix() では、0 に近いほうに切り上げられます (絶対的な方向は正の方向に移動しますが
、絶対値としては低くなります)。Fix(-3.5) は -3 になります。

Int() では、0 から遠ざかる方向に切り上げられます (絶対値としては大きくなりますが、
絶対的な方向は負の方向に移動します)。

四捨五入
切り捨てや切り上げを行うときには、返される数が、必ずしも元の数に最も近いとは限りません。
たとえば、1.9 を 1 に切り捨てた場合、2 に切り上げた場合よりも、元の数との差は大きくなります。
つまり、1.6 から 2.4 の間の数を 2 に丸めたほうが良いことがわかります。

しかし、1 と 2 のちょうど中間にある 1.5 の場合はどうでしょう? このような中間の数の場合、
慣例では切り上げることになっています。

中間の数を丸めるときには、対称的な方式で -0.5 を -1 に切り捨てるように実装することも、
非対称的な方式で -0.5 を 0 に切り上げるように実装することもできます。

以下に、対称な四捨五入を行う関数を示します。

Excel の Round() ワークシート関数
SQL Server の Round() 関数 

非対称的な四捨五入を行う関数は、以下のとおりです。

Java Math ライブラリの Round() メソッド 

VBA には、(算術的な) 四捨五入を行う関数はありません。

銀行型丸め
丸めを行った値どうしを加えるとき、.5 を同じ方向に丸めた場合には、
多くの数を加えるにつれて値の偏りが大きくなります。
この誤差を最小限に抑えるための方法の 1 つが銀行型丸め (Banker's Rounding) です。

銀行型丸めの場合、.5 は切り上げられることもあれば、切り捨てられることもあります。
数は最も近い偶数に丸められます。つまり 1.5 と 2.5 は両方とも 2 に丸められ、3.5 と 4.5 は
両方とも 4 に丸められます。銀行型丸めは対称的なアルゴリズムです。

VBA では、数値関数 CByte()、CInt()、CLng()、CCur()、および Round() が銀行型丸めを行います。

Excel のワークシート関数には銀行型丸めを行う関数はありません。

Random Rounding
銀行型丸めを使用しても合計に誤差が生じる可能性があります。この誤差を取り除く別の方法として、
.5 の切り上げや切り捨てを完全にランダムに行うという方法があります。この方法では、
故意にデータに偏りが与えられていたとしても、誤差は最小限に抑えられる可能性があります。
しかし、ランダムに分散したデータに対して Random Rounding を使用すると、
銀行型丸めを使用した場合よりも誤差が大きくなることもあります。
また、同じデータの合計を 2 回求めたときに、異なる結果が得られる可能性もあります。

マイクロソフト製品には、Random Rounding を行うプロシージャは実装されていません。

Alternate Rounding
Alternate Rounding は、連続的な呼び出し時に、.5 の切り上げと切り捨てを交互に行う方式です。

マイクロソフト製品には、Alternate Rounding を行うプロシージャは実装されていません。

製品によって異なる Round() 関数
Round() 関数の実装は、開発時期の違いから、マイクロソフト製品間で異なります。

以下の表は、製品と実装の関係を示したものです。

製品 実装
----------------------------------------------------------------------
Visual Basic for Applications 6.0 銀行型丸め
Excel ワークシート関数 対称的な四捨五入
SQL Server 対称的な四捨五入、または
対称的な切り捨て (Fix)。
(引数により異なる)

Java Math ライブラリ 非対称的な四捨五入


Visual Basic 6.0 および Visual Basic for Applications 6.0 の Round() 関数は銀行型丸めを実行します。
この関数にはオプションとして第 2 の引数があり、丸めを行う小数点以下の桁数を指定することができます。
次の例を参照してください。

Debug.Print Round(2.45, 1) を実行した結果は 2.4 です。



サンプル データ
次表には、いくつかのサンプル データと、そのデータに対して丸めを行った結果を、方式ごとに示します。
また、その次の表には各結果の合計を示します。

数/Int/Fix/Ceiling/四捨五入 (対称)/四捨五入 (非対称)/銀行型/Random/Alt.
---------------------------------------------------------------------
-2.6 -3 -2 -2 -3 -3 -3 -3 -3
-2.5 -3 -2 -2 -2 -3 -2 -2 -3
-2.4 -3 -2 -2 -2 -2 -2 -2 -2
-1.6 -2 -1 -1 -2 -2 -2 -2 -2
-1.5 -2 -1 -1 -1 -2 -2 -1 -1
-1.4 -2 -1 -1 -1 -1 -1 -1 -1
-0.6 -1 0 0 -1 -1 -1 -1 -1
-0.5 -1 0 0 0 -1 0 -1 -1
-0.4 -1 0 0 0 0 0 0 0
0.4 0 0 1 0 0 0 0 0
0.5 0 0 1 1 1 0 1 1
0.6 0 0 1 1 1 1 1 1
1.4 1 1 2 1 1 1 1 1
1.5 1 1 2 2 2 2 1 1
1.6 1 1 2 2 2 2 2 2
2.4 2 2 3 2 2 2 2 2
2.5 2 2 3 3 3 2 3 3
2.6 2 2 3 3 3 3 3 3


すべての結果の合計は、以下のとおりです。

数/Int/Fix/Ceiling/四捨五入 (対称)/四捨五入 (非対称)/銀行型/Random/Alt.
---------------------------------------------------------------------
0.0 -9 0 9 3 0 0 1 0


すべての負の数の合計は、以下のとおりです。

数/Int/Fix/Ceiling/四捨五入 (対称)/四捨五入 (非対称)/銀行型/Random/Alt.
---------------------------------------------------------------------
-13.5 -18 -9 -9 -12 -15 -13 -13 -14


すべての正の数の合計は、以下のとおりです。

数/Int/Fix/Ceiling/四捨五入 (対称)/四捨五入 (非対称)/銀行型/Random/Alt.
---------------------------------------------------------------------
13.5 9 9 18 15 15 13 14 14


上記の表を見ると、それぞれの丸め方式間の相違点がわかります。ランダムに分散した正の数と負の数では、
Fix()、四捨五入 (対称)、銀行型丸め、Alternate Rounding で、実際の合計との差が最も小さくなります。
Random Rounding の差も、それらに続いて小さくなっています。

しかし、数がすべて正の数である場合や、すべての負の数である場合には、
実際の合計との差が最も小さくなるのは銀行型丸め、Alternate Rounding 、および Random Rounding です。

ユーザー定義関数のサンプル
後述の「関数一覧表」のサンプル コードを使用することで、前述の各丸め方式を実装することができます。

提供される関数は次のとおりです。

AsymDown 非対称的な切り捨てを行います。Int() に類似。
負の数は、負の方向に切り捨てられます。

SymDown 対称的な切り捨てを行います。Fix() に類似。
すべての数を 0 の方向に切り捨てます。
正の数については AsymDown と同様です。

AsymUp 端数を非対称的に切り上げます
負の数については SymDown と同様です。
Ceiling に類似。

SymUp 端数を対称的に (0 から離れる方向に) 切り上げます。
正の数については AsymUp と同様です。
負の数については AsymDown と同様です。

AsymArith 非対称的に四捨五入します。.5 は常に切り上げられます。
Java ライブラリの Round 関数と同様です。

SymArith 対称的に四捨五入します。.5 は 0 から離れる方向に丸められます。
正の数についてはs AsymArith と同様です。
Excel の Round ワークシート関数に類似。

BRound 銀行型丸め。
.5 は最も近い偶数に丸められます。
定義上、対称です。

RandRound Random Rounding。
.5 の切り上げと切り捨ては、ランダムに行われます。

AltRound Alternate Rounding。
.5 の切り上げと切り捨ては、交互に行われます。

ATruncDigits AsyncTrunc と同様ですが、異なる引数を取ります。


これらの関数はいずれも、丸める数および 倍率 (省略可) の 2 つの引数を取ります。倍率を省略すると、
上記のいずれかの方法によって作成される整数が返されます。倍率を指定すると、
数は指定された倍率によって異なる丸め方で丸められます。たとえば、AsymArith(2.55, 10) の結果は 2.6 になります。
つまりこの関数では、1/倍率 (1/10 = 0.1) の位で丸められます。

注 : 倍率に 0 を指定すると、実行時エラーが発生します。これは、"1/倍率" が 1/0 となるためです。

次表には、いくつかの倍率を指定したときの結果を示します。

式 結果 コメント
--------------------------------------------------------------------
AsymArith(2.5) 3 次の整数に切り上げられます。
BRound(2.18, 20) 2.2 最も近い 5 セント (1/20 ドル) に丸められます。
SymDown(25, .1) 20 最も近い 10 の偶数倍の数に丸められます。


ADownDigits は上記の説明の例外です。これはテンプレート関数であり、
倍率ではなく小数点以下の桁数を指定することができます。

式 結果 コメント
---------------------------------------------------------------------
ADownDigits(2.18, 1) 2.1 次の 10 ^ -1 の倍数に切り下げられます。



関数一覧


Function AsymDown(ByVal X As Double, _
Optional ByVal Factor As Double = 1) As Double
AsymDown = Int(X * Factor) / Factor
End Function

Function SymDown(ByVal X As Double, _
Optional ByVal Factor As Double = 1) As Double
SymDown = Fix(X * Factor) / Factor
' 別の方法:
' SymDown = AsymDown(Abs(X), Factor) * Sgn(X)
End Function

Function AsymUp(ByVal X As Double, _
Optional ByVal Factor As Double = 1) As Double
Dim Temp As Double
Temp = Int(X * Factor)
AsymUp = (Temp + IIf(X = Temp, 0, 1)) / Factor
End Function

Function SymUp(ByVal X As Double, _
Optional ByVal Factor As Double = 1) As Double
Dim Temp As Double
Temp = Fix(X * Factor)
SymUp = (Temp + IIf(X = Temp, 0, Sgn(X))) / Factor
End Function

Function AsymArith(ByVal X As Double, _
Optional ByVal Factor As Double = 1) As Double
AsymArith = Int(X * Factor + 0.5) / Factor
End Function

Function SymArith(ByVal X As Double, _
Optional ByVal Factor As Double = 1) As Double
SymArith = Fix(X * Factor + 0.5 * Sgn(X)) / Factor
' 別の方法:
' SymArith = Abs(AsymArith(X, Factor)) * Sgn(X)
End Function

Function BRound(ByVal X As Double, _
Optional ByVal Factor As Double = 1) As Double
' 小さい数用:
' BRound = CLng(X * Factor) / Factor
Dim Temp As Double, FixTemp As Double
Temp = X * Factor
FixTemp = Fix(Temp + 0.5 * Sgn(X))
' 特別な方法で .5 の丸めを処理する
If Temp - Int(Temp) = 0.5 Then
If FixTemp / 2 <> Int(FixTemp / 2) Then ' Temp が奇数の場合
' 絶対値を 1 減らし、偶数にする。
FixTemp = FixTemp - Sgn(X)
End If
End If
BRound = FixTemp / Factor
End Function

Function RandRound(ByVal X As Double, _
Optional ByVal Factor As Double = 1) As Double
' 呼び出しの前にランダム化するステートメントを実行する必要がある。
Dim Temp As Double, FixTemp As Double
Temp = X * Factor
FixTemp = Fix(Temp + 0.5 * Sgn(X))
' 特別な方法で .5 の丸めを処理する。
If Temp - Int(Temp) = 0.5 Then
' 半分の割合で絶対値を 1 減らす。
FixTemp = FixTemp - Int(Rnd * 2) * Sgn(X)
End If
RandRound = FixTemp / Factor
End Function

Function AltRound(ByVal X As Double, _
Optional ByVal Factor As Double = 1) As Double
Static fReduce As Boolean
Dim Temp As Double, FixTemp As Double
Temp = X * Factor
FixTemp = Fix(Temp + 0.5 * Sgn(X))
' 特別な方法で .5 の丸めを処理する。
If Temp - Int(Temp) = 0.5 Then
' .5 の切り捨て (負)、切り上げ (正) を交互に行う。
If (fReduce And Sgn(X) = 1) Or (Not fReduce And Sgn(X) = -1) Then
' または、上記の If ステートメントを以下のように変更し、
' 絶対値の増減を交互に行って .5 を丸める。
'
' If fReduce Then
FixTemp = FixTemp - Sgn(X)
End If
fReduce = Not fReduce
End If
AltRound = FixTemp / Factor
End Function

Function ADownDigits(ByVal X As Double, _
Optional ByVal Digits As Integer = 0) As Double
ADownDigits = AsymDown(X, 10 ^ Digits)
End Function


注 : Excel の MRound() ワークシート関数を除き、丸めを行う組み込み関数は ADownDigits と同様の引数を取ります。
つまり、2 つ目の引数は倍率ではなく小数点以下の桁数を示します。

この資料に示した丸めを行う関数では、MRound() と同様に倍率が使用されます。この方式では、
丸める位の指定に 10 の累乗を使用する必要がないため、より柔軟に丸めを実行できます。
ADownDigits と同様のラッパー関数を作成することができます。

浮動小数点の限界
この資料で紹介したすべての丸めの実装は、倍精度浮動小数点型を使用するため、
およそ小数点以下 15 桁までの数を表すことができます。

すべての小数値を正確に表せるわけではないため、表示された値と保存された値の不一致に起因して、
正しい結果を得られないことがあります。

たとえば、2.25 は内部では 2.2499999... と保存されることがあります。
このとき、四捨五入で切り上げられるべきところを、切り捨てられることがあります。
また、数に対して行われる計算処理が多ければ多いほど、
保存されているバイナリ値が本来の小数値とかけ離れる可能性が大きくなります。

このような場合、通貨型などの異なるデータ型を選択することをお勧めします。
通貨型では、小数部分が 4 桁に限定されています。

あるいは、データ型を Variant にし、CDec() を使用して常に 10 進型への変換を行います。
これにより、小数部分を 28 桁に限定することができます。

通貨型の値を丸める
通貨型を使用すると、小数部分が 4 桁に限定されますが、セント用に小数部分を 2 桁に丸めるのが一般的です。

以下の Round2CB 関数は、小数点以下 2 桁で銀行型丸めを実行するハードコードされた例です。
この例では元の数に乗算を行いません。
これは、通貨値が通貨型の上限に近づいた場合に起こる可能性のあるオーバーフローを防ぎます。

Function Round2CB (ByVal X As Currency) As Currency
Round2CB = CCur(X / 100) * 100
End Function



10 進型の値を丸める
次に示すのは、10 進型を使用した非対称の四捨五入の例です。

Function AsymArithDec(ByVal X As Variant, _
Optional ByVal Factor As Variant = 1) As Variant
If Not IsNumeric(X) Then
AsymArithDec = X
Else
If Not IsNumeric(Factor) Then Factor = 1
AsymArithDec = Int(CDec(X * Factor) + .5)
End If
End Function



丸めの精度を落とす
数値を丸めるときには、学校で教わったように正の数を使用して四捨五入を行うのが普通です。
学校で習う方式の四捨五入では、丸める位の 1 桁下の数のみを使用し、それ以降の数については無視します。
つまり、値を手軽に丸めることができる代わりに、精度が落ちます。

たとえば、2.5 と 2.51 は共に 3 に切り上げられ、2.4 と 2.49 は共に 2 に切り捨てられます。

銀行型丸め (または .5 の切り上げや切り捨てを行う他の方法) を使用して、
あるいは非対称の四捨五入を使用して負の数を丸める場合、精度を落とすと最も近い数に丸められないことがあり、
誤った結果を生み出すことにつながります。

たとえば銀行型丸めでは、2.5 は 2 に切り捨てられ、2.51 は 3 に切り上げられます。

非対称の四捨五入を使用すると、-2.5 は -2 に切り上げられ、-2.51 は -3 に切り捨てられます。
http://support.microsoft.com/default.aspx?scid=kb;ja;JP196652