Question

J'ai une simple fonction C #:

public static double Floor(double value, double step)
{
    return Math.Floor(value / step) * step;
}

qui calcule le nombre supérieur, inférieur ou égal à « valeur », qui est multiple de « step ». Mais il manque de précision, comme on le voit dans les tests suivants:

[TestMethod()]
public void FloorTest()
{
    int decimals = 6;
    double value = 5F;
    double step = 2F;
    double expected = 4F;
    double actual = Class.Floor(value, step);
    Assert.AreEqual(expected, actual);
    value = -11.5F;
    step = 1.1F;
    expected = -12.1F;
    actual = Class.Floor(value, step);
    Assert.AreEqual(Math.Round(expected, decimals),Math.Round(actual, decimals));
    Assert.AreEqual(expected, actual);
}

La première et la deuxième sont ok affirme, mais le troisième échoue, car le résultat est seulement égale jusqu'à la 6e place décimale. Pourquoi donc? Y at-il moyen de corriger cela?

Mise à jour Si je déboguer le test, je vois que les valeurs sont égales jusqu'à la 8ème place décimale au lieu du 6, peut-être parce que Math.Round introduit une certaine imprécision.

Remarque Dans mon code de test je l'ai écrit le suffixe "F" (constante flottante explicite) où je voulais dire "D" (double), donc si je change que je peux avoir plus de précision.

Était-ce utile?

La solution

Si vous ne spécifiez pas tous les suffixes F (c.-à--12.1 au lieu de -12.1F) vous obtiendrez l'égalité à quelques chiffres plus. Vos constantes (et surtout les valeurs attendues) sont flotte maintenant en raison de la F. Si vous faites cela exprès alors s'il vous plaît expliquer.

Mais pour le reste je suis d'accord avec les autres réponses sur la comparaison des valeurs doubles ou flotteur pour l'égalité, il est tout simplement pas fiable.

Autres conseils

Je souhaite réellement sorte de ne pas avoir mis en œuvre l'opérateur == pour des flotteurs et des doubles. Il est presque toujours la mauvaise chose à faire pour demander jamais si un double ou un flotteur est égale à une autre valeur.

arithmétique en virgule flottante sur les ordinateurs ne sont pas science exacte :).

Si vous voulez une précision exacte à un nombre prédéfini de décimales utiliser décimal au lieu de double ou accepter un intervalle mineur.

http://en.wikipedia.org/wiki/Floating_point#Accuracy_problems

  

Par exemple, le non-représentabilité de 0,1 et 0,01 (en binaire) signifie que le résultat de la tentative de 0,1 carré est ni 0,01 ni le nombre représentable le plus proche.

Utilisez uniquement virgule flottante si vous voulez l'interprétation d'une machine (binaire) des systèmes numériques. Vous ne pouvez pas représenter 10 cents.

Si vous voulez précision, utilisez System.Decimal. Si vous voulez la vitesse, utilisez System.Double (ou System.Float). nombres à virgule flottante ne sont pas des « nombres précision infinie », et donc l'égalité affirmer doivent inclure une tolérance. Tant que vos numéros ont un nombre raisonnable de chiffres significatifs, cela est ok.

  • Si vous cherchez à faire des mathématiques sur de très grandes et très petit nombre, ne pas utiliser float ou double.
  • Si vous avez besoin de précision infinie, ne pas utiliser float ou double.
  • Si vous l'agrégation d'un très grand nombre de valeurs, ne pas utiliser float ou double (les erreurs seront eux-mêmes composés).
  • Si vous avez besoin de vitesse et la taille, en flottant ou double.

Voir cette réponse (également par moi) pour une analyse détaillée de la façon dont la précision affecte le résultat de vos opérations mathématiques.

Vérifiez les réponses à cette question: Est-il sûr de vérifier les valeurs à virgule flottante pour l'égalité à 0?

Vraiment, il suffit de cocher pour "la tolérance au sein de la ..."

et doubles flotteurs ne peuvent pas stocker avec précision tous les numéros. Ceci est une limitation du système de Virgule flottante IEEE. Afin d'avoir une précision fidèle, vous devez utiliser une bibliothèque de mathématiques plus avancé.

Si vous n'avez pas besoin de précision passé un certain point, alors peut-être décimal fonctionnera mieux pour vous. Il a une plus grande précision que le double.

Pour la question similaire, je finis à l'aide de la mise en œuvre suivante qui semble succès la plupart de mon cas de test (jusqu'à une précision de 5 chiffres):

public static double roundValue(double rawValue, double valueTick)
{
    if (valueTick <= 0.0) return 0.0;

    Decimal val = new Decimal(rawValue);
    Decimal step = new Decimal(valueTick);
    Decimal modulo = Decimal.Round(Decimal.Divide(val,step));

    return Decimal.ToDouble(Decimal.Multiply(modulo, step));
}

Parfois, le résultat est plus précis que vous attendez de stricte: FP IEEE 754. C'est parce que HW utilise plus de bits pour le calcul. Voir C # spécification et cet article

Java a mot-clé strictfp et C ++ ont des commutateurs du compilateur. Je manque cette option dans .NET

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top