ROUNDDOWN 関数の結果が Excel (2000以降)と異なる場合がある

文書番号 : 17290     文書種別 : 使用方法     最終更新日 : 2005/03/31
文書を印刷する
対象製品
El Tabelle for .NET 3.0J
詳細
WorkBook/Sheet コントロールの数式機能において、小数点以下の値を含む計算を ROUNDDOWN 関数で実行すると、ROUNDDOWN 関数の結果が Excel (2000以降)と異なるケースがあります。

具体的には

=ROUNDDOWN(800*40.8,0)

の結果が El Tabelle では 32639 ですが、Excel (2000以降)では 32640 です。
この現象は ROUNDDOWN 関数内で Double 型のデータで計算を行っていることによるもので、Double 型が小数点以下のデータを厳密に扱えない仕様に起因します。これは Excel 95/97 でも同様に確認できます。
El Tabelle と Excel で結果が異なるのは、Excel 2000 以降に ROUNDDOWN 関数に補正処理が組み込まれたためです。El Tabelle では関数内の処理を Decimal 型で行うことにより、Excel と同じ結果を得ることができます。

以下は ROUNDDOWN ユーザー定義関数を上書きするためのコードです。

[Visual Basic]
'ROUNDDOWNクラスからオブジェクトを作成する
Dim objRoundDown As ROUNDDOWN = New ROUNDDOWN()
'ユーザー定義関数のコレクションにオブジェクトを追加する
WorkBook1.CustomFunctions.Add(objRoundDown)

[C#]
//ROUNDDOWNクラスからオブジェクトを作成する
ROUNDDOWN objRoundDown = new ROUNDDOWN();
//ユーザー定義関数のコレクションにオブジェクトを追加する
workBook1.CustomFunctions.Add(objRoundDown);

以下は Decimal 型で処理を行う ROUNDDOWN ユーザー定義関数です。

[Visual Basic]
Imports GrapeCity.Data.FormulaService

'ROUNDDOWN関数のDecimal対応版
Public Class ROUNDDOWN
  Implements ICustomFunction

  Private m_ErrorMessage As String = Nothing
  Private m_hasError As Boolean = False

  Public ReadOnly Property Name() As String Implements ICustomFunction.Name
    Get
      '関数の名前
      Return "ROUNDDOWN"
    End Get
  End Property

  Public ReadOnly Property Description() As String Implements ICustomFunction.Description
    Get
      '関数の説明
      Return "Rounds a number down, toward zero."
    End Get
  End Property

  Public ReadOnly Property ErrorMessage() As String Implements ICustomFunction.ErrorMessage
    Get
      'エラーメッセージ
      Return m_ErrorMessage
    End Get
  End Property

  Public ReadOnly Property HasError() As Boolean Implements ICustomFunction.HasError
    Get
      'エラーの有無
      Return m_hasError
    End Get
  End Property

  Public Function Calculate(ByVal ParamArray List() As Object) As Object Implements ICustomFunction.Calculate
    '計算処理

    Dim i As Integer

    m_hasError = False

    If List.Length = 2 Then
      Dim mid1 As Decimal
      Dim result As Decimal

      If List(1) Is System.DBNull.Value Then
        mid1 = 0
      Else
        mid1 = CDec(List(1))
      End If

      If List(1) < 16 Then
        Dim test As Decimal = Math.Pow(10, mid1)
        Dim test1 As Decimal = CDec(List(0) * test)
        If test1 < Decimal.MaxValue And test1 > Decimal.MinValue Then
          Dim test2 As Decimal = test1
          result = test2 / test
        Else
          result = List(0)
        End If
      Else
        result = CDec(List(0))
      End If

      Return result
    Else
      m_ErrorMessage = "パラメータの数が不正です"
      m_hasError = True
    End If
    Return Nothing
  End Function

  Public ReadOnly Property ParamType() As Type() Implements ICustomFunction.ParamType
    Get
      '関数のパラメータ
      Return New Type() {GetType(Decimal), GetType(Integer)}
    End Get
  End Property

  Public ReadOnly Property ReturnType() As Type Implements ICustomFunction.ReturnType
    Get
      '関数の戻り値
      Return GetType(Decimal)
    End Get
  End Property

End Class

[C#]
using GrapeCity.Win.ElTabelle;

namespace Rounddown
{
  ///
  /// ROUNDDOWN関数のDecimal対応版
  ///

  public class ROUNDDOWN : ICustomFunction
  {
    private string m_ErrorMessage = null;
    private bool m_hasError = false;

    public ROUNDDOWN()
    {
      //
      // TODO: コンストラクタ ロジックをここに追加してください。
      //
    }

    public string Name
    {
      get
      {
        //関数の名前
        return "ROUNDDOWN";
      }
    }

    public string Description
    {
      get
      {
        //関数の説明
        return "Rounds a number down, toward zero.";
      }
    }

    public string ErrorMessage
    {
      get
      {
        //エラーメッセージ
        return m_ErrorMessage;
      }
    }

    public bool HasError
    {
      get
      {
        //エラーの有無
        return m_hasError;
      }
    }

    public object Calculate (params object[] List)
    {
      //計算処理

      m_hasError = false;
      if (List.Length == 2)
      {
        decimal mid1;
        decimal result;
        if(List[1] == null)
        {
          mid1 = 0;
        }
        else
        {
          mid1 = (decimal)float.Parse(List[1].ToString());
        }
      
        if(((int)float.Parse(List[1].ToString())) < (int)16)
        {
          decimal test = (decimal)Math.Pow(10, (double)mid1);
          decimal test1 = (decimal)float.Parse(List[0].ToString()) * test;
          if((test1 < decimal.MaxValue) & (test1 > decimal.MinValue))
          {
            decimal test2 = test1;
            result = test2 / test;
          }
          else
          {
            result = (decimal)List[0];
          }
        }
        else
        {
          result = (decimal)List[0];
        }
        return result;
      }
      else
      {
        m_ErrorMessage = "パラメータの数が不正です";
        m_hasError = true;
      }
      return null;
    }

    public Type[] ParamType
    {
      get
      {
        //関数のパラメータ
        return new Type [] { typeof(double), typeof(double) };
      }
    }

    public Type ReturnType
    {
      get
      {
        //関数の戻り値
        return typeof(double);
      }
    }
  }
}

なお、製品に付属のサンプル(製品インストールフォルダ¥Samples¥WorkBookSheet 内)にこのコードを使用したサンプルプロジェクト「Rounddown」が含まれています。
キーワード
問題

この文書は、以前は次のFAQ IDで公開されていました : 6899