web-dev-qa-db-fra.com

Comment comparer correctement les valeurs décimales en C #?

Je viens d'un arrière-plan en C++, et je sais que vous ne pouvez pas comparer avec précision les flottants pour l'égalité. Pour C #, j'ai simplement supposé que la même politique s'applique aux valeurs décimales ou à toute valeur à virgule flottante en général.

Fondamentalement, j'ai deux valeurs décimales et si elles ne sont pas égales, j'ai besoin d'effectuer une action. par exemple.:

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

Si cela ne fonctionne pas comme prévu, je suis prêt à accepter une solution qui fait une comparaison d'égalité avec une marge d'erreur, disons comme .00001 ou quelque chose comme ça. Quelle serait la solution recommandée à ce problème?

46
void.pointer

Votre code fonctionnera comme prévu. C # decimals sont optimisés pour être très précis dans la représentation des nombres de base 10, donc si c'est ce que vous comparez (argent, ...), tout devrait bien se passer.

Voici une explication très claire sur la précision des décimales par Jon Skeet:

Différence entre décimal, float et double en .NET?

29
fretje

J'enquêtais sur quelque chose de similaire, mais avec une précision au lieu d'une marge d'erreur et j'ai fini par écrire des extensions pour Float. Cependant, cela peut facilement être adapté à tout type. J'ai une série compliquée de comparaisons et cela le rend agréable et lisible.

/// <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);
    } 
}
5
Kim

Je suis d'accord avec les autres réponses, mais je rencontre un problème où une décimale côté serveur "authentique" est comparée à celle provenant de JSON/navigateur (et doit avoir été un flottant à un moment donné).

Je me suis retrouvé avec ce code pour arrondir à 2 chiffres après la virgule décimale, ce qui était assez précis dans mon cas:

if (Decimal.Round(serverTotalPrice, 2) != Decimal.Round(request.TotalPrice, 2)) {
    throw new ArgumentException("The submitted Total Price is not valid");
}
4
Ekus

Je pense que this résoudra votre problème.

Fondamentalement, il existe une méthode decimal.compare.

EDIT: Cela peut être la meilleure méthode:

Decimal.Equals

EDIT2: Si vous pouvez comparer directement comme suggéré ci-dessus, cela peut être plus efficace. Je laisserai cela comme il peut être intéressant.

2
nycdan