“For money, always decimal”?

让人想犯罪 __ 提交于 2019-12-12 07:48:12

问题


Well, the rule "For money, always decimal" isn't applied inside the Microsoft development team, because if it was:

Namespace: Microsoft.VisualBasic
Assembly:  Microsoft.VisualBasic (in Microsoft.VisualBasic.dll)

Financial.IPmt and all the other methods would receive/return decimal and not double as it is.

Now I wonder if I can use these methods without worry with round mistakes?

Should I use some other libraries to work with finances? If yes, could you point me some good ones (for C# use) ?


回答1:


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
}



回答2:


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)

  • Implementations of excel financial functions in F# - can be used from any .NET language, does not require F# reference

  • QuantLib may be able to be exported/generated for C#. See this SO question/answer for more info




回答3:


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.



来源:https://stackoverflow.com/questions/2604003/for-money-always-decimal

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