“For money, always decimal”?

爱⌒轻易说出口 提交于 2019-12-03 12:16:50

You can use this class:

public class Financial
{
    #region Methods

    public static decimal IPmt(decimal Rate, decimal Per, decimal NPer, decimal PV, decimal FV, FinancialEnumDueDate Due)
    {
        decimal num;
        if (Due != FinancialEnumDueDate.EndOfPeriod)
        {
            num = 2;
        }
        else
        {
            num = 1;
        }
        if ((Per <= 0) || (Per >= (NPer + 1)))
        {
            //Argument_InvalidValue1=

            throw new ArgumentException("Argument 'Per' is not a valid value.");
        }
        if ((Due != FinancialEnumDueDate.EndOfPeriod) && (Per == 1))
        {
            return 0;
        }
        decimal pmt = Pmt(Rate, NPer, PV, FV, Due);
        if (Due != FinancialEnumDueDate.EndOfPeriod)
        {
            PV += pmt;
        }
        return (FV_Internal(Rate, Per - num, pmt, PV, FinancialEnumDueDate.EndOfPeriod) * Rate);
    }

    public static decimal PPmt(decimal Rate, decimal Per, decimal NPer, decimal PV, decimal FV, FinancialEnumDueDate Due)
    {
        if ((Per <= 0) || (Per >= (NPer + 1)))
        {
            throw new ArgumentException("Argument 'Per' is not valid.");
        }
        decimal num2 = Pmt(Rate, NPer, PV, FV, Due);
        decimal num = IPmt(Rate, Per, NPer, PV, FV, Due);
        return (num2 - num);
    }

    static decimal FV_Internal(decimal Rate, decimal NPer, decimal Pmt, decimal PV, FinancialEnumDueDate Due)
    {
        decimal num;
        if (Rate == 0)
        {
            return (-PV - (Pmt * NPer));
        }
        if (Due != FinancialEnumDueDate.EndOfPeriod)
        {
            num = 1 + Rate;
        }
        else
        {
            num = 1;
        }
        decimal x = 1 + Rate;
        decimal num2 = (decimal)Math.Pow((double)x, (double)NPer);
        return ((-PV * num2) - (((Pmt / Rate) * num) * (num2 - 1)));
    }

    static decimal Pmt(decimal Rate, decimal NPer, decimal PV, decimal FV, FinancialEnumDueDate Due)
    {
        decimal num;
        if (NPer == 0)
        {
            throw new ArgumentException("Argument NPer is not a valid value.");
        }
        if (Rate == 0)
        {
            return ((-FV - PV) / NPer);
        }
        if (Due != FinancialEnumDueDate.EndOfPeriod)
        {
            num = 1 + Rate;
        }
        else
        {
            num = 1;
        }
        decimal x = Rate + 1;
        decimal num2 = (decimal)Math.Pow((double)x, (double)NPer);
        return (((-FV - (PV * num2)) / (num * (num2 - 1))) * Rate);
    }

    #endregion Methods
}
Daniel Schaffer

Here's an interesting discussion regarding exactly this topic: http://www.vbforums.com/showthread.php?t=524101

About 1/3 of the way down someone explains that it uses Double because the VB.NET functions were implemented to work exactly the same as VB6. VB6 doesn't have a decimal type, which is why it uses double.

So, it seems that if accuracy is important, you should not use these functions.

The answers to this question have some promising alternatives - just ignore the accepted answer that suggests using the VB library.

The previously linked question has been deleted, so here are some of the suggestions I was referencing (note: I have not tried these, YMMV)

Jeffrey Hantin

The rule to use decimal for money is helpful because most currencies have decimal units. By using decimal arithmetic, you avoid introducing and accumulating round-off error.

Financial Class functions use floating-point for a few reasons:

  • They don't internally accumulate -- they are based on a closed-form exponential/logarithmic computation, not iteration and summation over periods.
  • They tend not to use or produce exact decimal values. For example, an exact decimal annual interest rate divided by 12 monthly payments becomes a repeating decimal.
  • They are intended primarily for decision support, and in the end have little applicability to actual bookkeeping.

Pmt and rounding may determine the nominal monthly payment, but once that amount is determined, balance accumulation -- payments made, interest charges applied, etc. -- happens in decimal. Also, late or advance payments, payment holidays, and other such non-uniformities would invalidate the projected amortization provided by the financial functions.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!