Как правильно сравнить десятичные значения в C #? - PullRequest
44 голосов
/ 09 мая 2011

Я пришел из C ++ и знаю, что вы не можете точно сравнить числа с плавающей точкой на равенство. Для C # я просто предположил, что та же самая политика применяется к десятичным значениям или к любому значению с плавающей запятой вообще.

По сути, у меня есть два десятичных значения, и если они НЕ равны друг другу, мне нужно выполнить какое-то действие. e.g.:

decimal value1, value2;
// Assume value1 and value2 are set somewhere to valid values.
if( value1 != value2 )
{
    // Do something
}

Если это не сработает, как ожидалось, я готов принять решение, которое сравнивает равенство с пределом погрешности, скажем, как .001001 или что-то в этом роде. Каково было бы рекомендуемое решение этой проблемы?

Ответы [ 4 ]

28 голосов
/ 09 мая 2011

Ваш код будет работать как положено. C # decimal s оптимизированы, чтобы быть очень точными при представлении чисел из 10 оснований, поэтому, если вы сравниваете это (деньги, ...), все должно быть хорошо.

Вот очень четкое объяснение о точности десятичных знаков Джоном Скитом:

Разница между десятичной, плавающей и двойной в .NET?

5 голосов
/ 22 декабря 2011

Я исследовал нечто подобное, но с точностью, а не с погрешностью, и в итоге написал несколько расширений для Float.Это может быть легко адаптировано для любого типа, хотя.У меня сложная серия сравнений, и это делает ее красивой и удобочитаемой.

/// <summary>
/// A set of extensions to allow the convenient comparison of float values based on a given precision.
/// </summary>
public static class FloatingPointExtensions
{
    /// <summary>
    /// Determines if the float value is less than or equal to the float parameter according to the defined precision.
    /// </summary>
    /// <param name="float1">The float1.</param>
    /// <param name="float2">The float2.</param>
    /// <param name="precision">The precision.  The number of digits after the decimal that will be considered when comparing.</param>
    /// <returns></returns>
    public static bool LessThan(this float float1, float float2, int precision)
    {
        return (System.Math.Round(float1 - float2, precision) < 0);
    }

    /// <summary>
    /// Determines if the float value is less than or equal to the float parameter according to the defined precision.
    /// </summary>
    /// <param name="float1">The float1.</param>
    /// <param name="float2">The float2.</param>
    /// <param name="precision">The precision.  The number of digits after the decimal that will be considered when comparing.</param>
    /// <returns></returns>
    public static bool LessThanOrEqualTo(this float float1, float float2, int precision)
    {
        return (System.Math.Round(float1 - float2, precision) <= 0);
    }

    /// <summary>
    /// Determines if the float value is greater than (>) the float parameter according to the defined precision.
    /// </summary>
    /// <param name="float1">The float1.</param>
    /// <param name="float2">The float2.</param>
    /// <param name="precision">The precision.  The number of digits after the decimal that will be considered when comparing.</param>
    /// <returns></returns>
    public static bool GreaterThan(this float float1, float float2, int precision)
    {
        return (System.Math.Round(float1 - float2, precision) > 0);
    }

    /// <summary>
    /// Determines if the float value is greater than or equal to (>=) the float parameter according to the defined precision.
    /// </summary>
    /// <param name="float1">The float1.</param>
    /// <param name="float2">The float2.</param>
    /// <param name="precision">The precision.  The number of digits after the decimal that will be considered when comparing.</param>
    /// <returns></returns>
    public static bool GreaterThanOrEqualTo(this float float1, float float2, int precision)
    {
        return (System.Math.Round(float1 - float2, precision) >= 0);
    }

    /// <summary>
    /// Determines if the float value is equal to (==) the float parameter according to the defined precision.
    /// </summary>
    /// <param name="float1">The float1.</param>
    /// <param name="float2">The float2.</param>
    /// <param name="precision">The precision.  The number of digits after the decimal that will be considered when comparing.</param>
    /// <returns></returns>
    public static bool AlmostEquals(this float float1, float float2, int precision)
    {
        return (System.Math.Round(float1 - float2, precision) == 0);
    } 
}
4 голосов
/ 15 июля 2015

Я согласен с другими ответами, но сталкиваюсь с проблемой, когда один «подлинный» десятичный раздел на стороне сервера сравнивается с одним из JSON / браузера (и в какой-то момент должен был быть плавающим).

Я получил этот код с округлением до 2 цифр после десятичной точки, что было достаточно точно в моем случае:

if (Decimal.Round(serverTotalPrice, 2) != Decimal.Round(request.TotalPrice, 2)) {
    throw new ArgumentException("The submitted Total Price is not valid");
}
2 голосов
/ 09 мая 2011

Я думаю это решит вашу проблему.

В основном есть метод decimal.compare.

РЕДАКТИРОВАТЬ: Это может быть лучший метод:

Decimal.Equals

РЕДАКТИРОВАТЬ2: Если вы можете сравнить непосредственно, как предложено выше, это может быть более эффективным. Я оставлю это, поскольку это может представлять интерес.

...