[VB.NET] 切り捨て/切り上げと床関数/天井関数,四捨五入と偶数丸め

2014年1月1日

 プログラム中で切り上げ/切り捨てをしたい場合があります。

 切り捨てをする場合は Math.Floor、切り上げをする場合は Math.Ceiling、という関数を使用すると良い、という話を聞くわけですが、これらの関数、丸めの桁の指定ができないんですね。四捨五入などをする Math.Round では第二引数で桁を指定することができるのに…

 なぜかというと、この Ceiling とか Floor。切り捨てや切り上げのための関数ではなくて、床関数と天井関数というれっきとした数学関数らしいです。曰く「任意の実数に対し整数を対応付ける関数」だそうで。"対応する整数"を求めるわけだから、丸めの桁の指定などあるわけが…という話のようです(汗
 http://ja.wikipedia.org/wiki/床関数と天井関数

 しかし実務では、床関数とかは手段でしかなく「指定桁数で丸めたい」という要求のほうが大きいです。
 結局、結論としては、有効桁小数部2位なら、従来通り以下の手続きを行う必要があります。

  1. まず100倍する。
  2. これらの関数を利用して小数部を切り落とし。
  3. 再び1/100に。

 あれだけ膨大なクラスライブラリがあるんだからこの程度やってくれても…と思うわけですが、いまだに標準ライブラリではサポートされてはいない模様です…(汗

 ・・・

 ところで一口に"丸め"といっても、代表的なもので以下の3種6パターンがあります。

  1. 切り捨て
    1. 負の無限大への丸め
      … Math.Floor
    2. 0 への丸め
      … Math.Truncate
  2. 切り上げ
    1. 正の無限大への丸め
      … Math.Ceiling
    2. 無限大への丸め
      … 対応なし
  3. 最接近丸め
    1. 四捨五入
      … Math.Round(raw, digit, MidpointRounding.AwayFromZero)
    2. 偶数丸め(JIS 丸め)
      …Math.Round(raw, digit)

 切り上げの a と b、切り捨ての a と b は、正の数の領域では結果が同じです。ですが、負の数の領域で結果が異なります。
 a 側は、元の数を切り捨ての場合は上回らない、切り上げの場合は下回らないように丸めます。たとえば -3.5 という数値があった場合、切り捨てでは -4 が、切り上げでは -3 が得られます。
 b 側は、絶対値で切り捨て/切り上げをします。-3.5 という数値の場合は、切り捨てでは -3 が、切り上げでは -4 が得られます。直感的にはこちらのほうが理解しやすいですが、"切り上げた値<切り下げた値" と大小関係が逆になる点には注意です。あと 0 付近の区間の幅が他の区間の幅と異なる点にも注意です。C 言語の int でキャストした場合などで得られるのはこちらの切り捨てです。(なので int をキャストして 0 となる場合、元の値は -1 < x < 1 の区間で他の数値の場合の 2 倍の幅があります)

 最接近丸めはいわゆる四捨五入です。ですが VB や C# などでは単純な四捨五入ではなく、偶数丸めと呼ばれるものがデフォルトになっています。(CInt とか Convert.ToInt32 とか。ただし文字列に変換する場合(String.Format)とかは、普通に四捨五入です)
 偶数丸めとは四捨六入で、五の場合は結果が偶数になるように切り捨てたり切り上げたりする方法です。
 このため、普通の四捨五入をする場合には、MidpointRounding.AwayFromZero オプションの指定を必要とする点に注意です。

 あと、手入力などの値を指定桁数に制限したい場合は、正規表現(たとえば "^[+|-]?[0-9]+(.[0-9]{1,2})?$" みたいな)などで入力文字列が正しいかを確認し、丸め処理でごまかさないように注意します。

 以下コード的なまとめ。

''' <summary>
''' 丸め
''' </summary>
''' <remarks>
''' 参考)
'''  http://ja.wikipedia.org/wiki/%E7%AB%AF%E6%95%B0%E5%87%A6%E7%90%86#.E4.B8.B8.E3.82.81.E3.81.AE.E7.A8.AE.E9.A1.9E
'''  http://rurema.clear-code.com/2.1.0/method/BigDecimal/s/mode.html
''' </remarks>
Public Class Rounding
    
    ''' <summary>
    ''' 切り上げ(指定値を下回らない最小値 / RM: Rounding toward Minus infinity)
    ''' </summary>
    Public Shared Function Ceiling(value As Double, digits As Integer)
        Dim shift = 10 ^ digits
        Return Math.Ceiling(value * shift) / shift
    End Function

    ''' <summary>
    ''' 切り捨て(指定値を上回らない最大値 / RP: Rounding toward Plus infinity)
    ''' </summary>
    Public Shared Function Floor(value As Double, digits As Integer)
        Dim shift = 10 ^ digits
        Return Math.Floor(value * shift) / shift
    End Function

    ''' <summary>
    ''' 切り捨て(0 に近い側 / RZ: Rounding toward Zero)
    ''' </summary>
    Public Shared Function Down(value As Double, digits As Integer)
        Dim shift = 10 ^ digits
        Return Math.Truncate(value * shift) / shift
    End Function

    ''' <summary>
    ''' 切り上げ(0 に遠い側 / RI: Rounding toward Infinity)
    ''' </summary>
    Public Shared Function Up(value As Double, digits As Integer)
        Dim shift = 10 ^ digits
        Dim v = value * shift
        Return If(v < 0, Math.Floor(v), Math.Ceiling(v)) / shift
    End Function

    ''' <summary>
    ''' 四捨五入(R: Round)
    ''' </summary>
    Public Shared Function HalfUp(value As Double, digits As Integer)
        Return Math.Round(value, digits, MidpointRounding.AwayFromZero)
    End Function

    ''' <summary>
    ''' 偶数丸め(RN: Round to the Nearest even)
    ''' </summary>
    Public Shared Function HalfEven(value As Double, digits As Integer)
        Return Math.Round(value, digits)
    End Function

End Class

 参考) http://ja.wikipedia.org/wiki/端数処理






カテゴリー: Program, VB.NET

Follow comments via the RSS Feed | Leave a comment | Trackback URL

コメントを投稿する

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)


«   »
 
Powered by Wordpress and MySQL. Theme by Shlomi Noach, openark.org