"За деньги, всегда десятичные"?

Хорошо, правило "За деньги, всегда десятичное" не применяется внутри команды разработчиков Microsoft, потому что если это было:

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

Financial.IPmt, и все остальные методы получат/возвращают decimal, а не double, как есть.

Теперь я задаюсь вопросом, могу ли я использовать эти методы без проблем с круглыми ошибками?

Должен ли я использовать некоторые другие библиотеки для работы с финансами? Если да, можете ли вы указать мне хорошие (для C# использования)?

Ответ 1

Вы можете использовать этот класс:

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

Здесь интересное обсуждение именно этой темы: http://www.vbforums.com/showthread.php?t=524101

Около 1/3 пути вниз кто-то объясняет, что он использует Double, потому что функции VB.NET были реализованы так же, как VB6. VB6 не имеет десятичного типа, поэтому он использует double.

Итак, кажется, что если точность важна, вы не должны использовать эти функции.

В ответах на этот вопрос есть несколько перспективных альтернатив - просто проигнорируйте принятый ответ, который предлагает использовать библиотеку VB.

Ранее связанный вопрос был удален, так что вот некоторые из предложений, на которые я ссылался (примечание: я не пробовал эти, YMMV)

Ответ 3

Правило использовать decimal за деньги полезно, потому что большинство валют имеют десятичные единицы. Используя десятичную арифметику, вы избегаете введения и накопления ошибки округления.

Функции финансового класса используют с плавающей запятой по нескольким причинам:

  • Они не накапливаются внутри - они основаны на экспоненциальном/логарифмическом вычислении замкнутой формы, а не на итерации и суммировании по периодам.
  • Они склонны не использовать или не давать точные десятичные значения. Например, точная десятичная годовая процентная ставка, деленная на 12 ежемесячных платежей, становится повторяющейся десятичной.
  • Они предназначены в первую очередь для поддержки принятия решений и, в конце концов, мало применимы к фактической бухгалтерии.

Pmt, а округление может определять номинальный ежемесячный платеж, но после определения этой суммы происходит накопление баланса - произведенные платежи, наложенные проценты и т.д. - происходит в decimal. Кроме того, поздние или авансовые платежи, праздничные дни и другие подобные неравномерности аннулируют прогнозируемую амортизацию, предоставляемую финансовыми функциями.