丸めを行うカスタム プロシージャを実装する方法

文書翻訳 文書翻訳
文書番号: 196652 - 対象製品
この記事は、以前は次の ID で公開されていました: JP196652
すべて展開する | すべて折りたたむ

目次

概要

マイクロソフト製品で使用可能な丸めのアルゴリズムは多数存在します。丸めのアルゴリズムには、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 から遠い方に丸められます (絶対値としては大きくなりますが、絶対的な意味では切り捨てです)。Int(-3.5) は -4 を返します。

算術型丸め

切り捨てや切り上げを行うときには、返される数が必ずしも元の数に最も近いとは限りません。たとえば、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 のワークシート関数には銀行型丸めを行う関数はありません。

ランダム丸め

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

マイクロソフト製品には、ランダム丸めのような丸めを行うプロシージャは実装されていません。

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 の引数があり、値を丸めるときの小数点以下の有効桁数を指定することができます。たとえば、次のコードは 2.4 を返します。
   Debug.Print Round(2.45, 1)
				

サンプル データ

次の表は、いくつかのサンプル データをさまざまな方式で丸めた結果を示したものです。
   サンプル/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 で、実際の合計との差が最も小さくなります。ランダム丸めの差も、それらに続いて小さくなっています。

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

ユーザー定義関数のサンプル

この資料の「関数一覧表」のサンプル コードを使用することで、この資料に記載されているそれぞれの丸め方式を実装することができます。

「関数一覧表」には、次の関数が記載されています。
   AsymDown      非対称的な切り捨てを行います。Int() に似ています。
                   負の数は、負の方向に切り捨てられます。
   SymDown       対称的な切り捨てを行います。Fix() に似ています。
                   すべての数を 0 の方向に丸めます。
                   正の数については AsymDown と同じです。
   AsymUp        端数を非対称的に切り上げます
                   負の数については SymDown と同じです。
                   Ceiling に似ています。
   SymUp         端数を対称的に、0 から離れる方向に丸めます。
                   正の数については AsymUp と同じです。
                   負の数については AsymDown と同じです。
   AsymArith     非対称的な算術型丸めを行います。.5 は常に切り上げられます。
                   Java ライブラリの Round 関数に似ています。
   SymArith      対称的な算術型丸めを行います。.5 は 0 から離れる方向に丸められます。
                   正の数については AsymArith と同じです。
                   Excel の Round ワークシート関数に似ています。
   BRound        銀行型丸めを行います。
                   .5 は最も近い偶数に丸められます。
                   定義上、対称です。
   RandRound     ランダム丸めを行います。
                   .5 は切り上げと切り捨てがランダムに行われます。
   AltRound      Alternate Rounding を行います。
                   .5 は切り上げと切り捨てが交互に行われます。
   ADownDigits   AsymDown と同じですが、引数が異なります。
				

これらの関数はいずれも、丸める数と除数 (省略可) の 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
   '  Alternately:
   '  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
   '  Alternately:
   '  SymArith = Abs(AsymArith(X, Factor)) * Sgn(X)
   End Function

   Function BRound(ByVal X As Double, _
            Optional ByVal Factor As Double = 1) As Double
   '  For smaller numbers:
   '  BRound = CLng(X * Factor) / Factor
     Dim Temp As Double, FixTemp As Double
     Temp = X * Factor
     FixTemp = Fix(Temp + 0.5 * Sgn(X))
     ' Handle rounding of .5 in a special manner
     If Temp - Int(Temp) = 0.5 Then
       If FixTemp / 2 <> Int(FixTemp / 2) Then ' Is Temp odd
         ' Reduce Magnitude by 1 to make even
         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
   ' Should Execute Randomize statement somewhere prior to calling.
     Dim Temp As Double, FixTemp As Double
     Temp = X * Factor
     FixTemp = Fix(Temp + 0.5 * Sgn(X))
     ' Handle rounding of .5 in a special manner.
     If Temp - Int(Temp) = 0.5 Then
       ' Reduce Magnitude by 1 in half the cases.
       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))
     ' Handle rounding of .5 in a special manner.
     If Temp - Int(Temp) = 0.5 Then
       ' Alternate between rounding .5 down (negative) and up (positive).
       If (fReduce And Sgn(X) = 1) Or (Not fReduce And Sgn(X) = -1) Then
       ' Or, replace the previous If statement with the following to
       ' alternate between rounding .5 to reduce magnitude and increase
       ' magnitude.
       ' 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() と同様に除数 (Excel の MRound() ワークシート関数では "倍数" と呼ばれています) が使用されます。この方式では、丸める位の指定に 10 の累乗を使用する必要がないため、より柔軟に丸めを実行できます。ADownDigits と同様のラッパー関数を作成することができます。

浮動小数点の限界

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

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

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

このような場合、通貨型 (Currency) などの異なるデータ型を選択できます。通貨型では、小数部分が 4 桁に限定されます。

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

通貨型 (Currency) の値を丸める

通貨型 (Currency) を使用すると、小数部分が 4 桁に限定されますが、米国の通貨単位であるセントに合わせて小数部分を 2 桁に丸めるのが一般的です。

以下の Round2CB 関数は、銀行型丸めによって小数部分を 2 桁に丸めるようにハードコードされています。この関数は、元の数に乗算を行いません。これにより、通貨値が通貨型の上限に近づいた場合に起こる可能性のあるオーバーフローを防ぎます。
   Function Round2CB (ByVal X As Currency) As Currency
     Round2CB = CCur(X / 100) * 100
   End Function
				

10 進型 (Decimal) の値を丸める

次に示すのは、10 進型 (Decimal) を使用した非対称的な算術型丸めの例です。
   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 に切り捨てられます。

この資料で紹介したユーザー定義関数では、対象となる数を丸める際にすべての桁を考慮に入れています。

関連情報

Visual Basic 6.0 ヘルプのトピック「Int、Fix 関数」、「Round 関数」

Microsoft Transact SQL リファレンスのトピック「ROUND」、「FLOOR」、「CEILING」

(c) Microsoft Corporation 1998, All Rights Reserved. Contributions by Malcolm Stewart, Microsoft Corporation.

関連情報

この資料は米国 Microsoft Corporation から提供されている Knowledge Base の Article ID 196652 (最終更新日 2004-07-15) を基に作成したものです。

この資料に含まれているサンプル コード/プログラムは英語版を前提に書かれたものをありのままに記述しており、日本語環境での動作は確認されておりません。

プロパティ

文書番号: 196652 - 最終更新日: 2004年11月17日 - リビジョン: 3.2
この資料は以下の製品について記述したものです。
  • Microsoft Visual Basic Control Creation Edition
  • Microsoft Visual Basic 5.0 Learning Edition
  • Microsoft Visual Basic 6.0 Learning Edition
  • Microsoft Visual Basic 5.0 Professional Edition
  • Microsoft Visual Basic 6.0 Professional Edition
  • Microsoft Visual Basic 5.0 Enterprise Edition
  • Microsoft Visual Basic 6.0 Enterprise Edition
  • Microsoft Visual Basic for Applications 5.0
  • Microsoft Visual Basic for Applications 6.0
  • Microsoft SQL Server 6.0 Standard Edition
  • Microsoft SQL Server 6.5 Standard Edition
  • Microsoft SQL Server 7.0 Standard Edition
キーワード:?
kbhowto KB196652
"Microsoft Knowledge Baseに含まれている情報は、いかなる保証もない現状ベースで提供されるものです。Microsoft Corporation及びその関連会社は、市場性および特定の目的への適合性を含めて、明示的にも黙示的にも、一切の保証をいたしません。さらに、Microsoft Corporation及びその関連会社は、本文書に含まれている情報の使用及び使用結果につき、正確性、真実性等、いかなる表明・保証も行ないません。Microsoft Corporation、その関連会社及びこれらの権限ある代理人による口頭または書面による一切の情報提供またはアドバイスは、保証を意味するものではなく、かつ上記免責条項の範囲を狭めるものではありません。Microsoft Corporation、その関連会社 及びこれらの者の供給者は、直接的、間接的、偶発的、結果的損害、逸失利益、懲罰的損害、または特別損害を含む全ての損害に対して、状況のいかんを問わず一切責任を負いません。(Microsoft Corporation、その関連会社 またはこれらの者の供給者がかかる損害の発生可能性を了知している場合を含みます。) 結果的損害または偶発的損害に対する責任の免除または制限を認めていない地域においては、上記制限が適用されない場合があります。なお、本文書においては、文書の体裁上の都合により製品名の表記において商標登録表示、その他の商標表示を省略している場合がありますので、予めご了解ください。"

フィードバック

 

Contact us for more help

Contact us for more help
Connect with Answer Desk for expert help.
Get more support from smallbusiness.support.microsoft.com