Quelle est la méthode la plus efficace pour la comparaison flottante et double?

quel serait le moyen le plus efficace de comparer deux valeurs double ou deux valeurs float ?

faire simplement ceci n'est pas correct:

bool CompareDoubles1 (double A, double B)
{
   return A == B;
}

mais quelque chose comme:

bool CompareDoubles2 (double A, double B) 
{
   diff = A - B;
   return (diff < EPSILON) && (-diff < EPSILON);
}

semble être un traitement des déchets.

est-ce que quelqu'un connaît un compareur de flotteurs plus intelligent?

447
demandé sur Kirill Kobelev 2008-08-20 06:09:33

27 réponses

soyez extrêmement prudent en utilisant l'une des autres suggestions. Tout dépend du contexte.

j'ai passé beaucoup de temps à traquer un bogue dans un système qui supposait a==b si |a-b|<epsilon . Les problèmes sous-jacents étaient:

  1. la présomption implicite dans un algorithme que si a==b et b==c puis a==c .

  2. utilisant le même epsilon pour les lignes mesurées en pouces et les lignes mesurées en mils (.001 po). C'est-à-dire a==b mais 1000a!=1000b . (C'est pourquoi Almostequal2scomplément demande l'epsilon ou max ULPS).

  3. l'utilisation du même epsilon à la fois pour le cosinus des angles et la longueur des lignes!

  4. utilisation d'une telle fonction de comparaison pour trier des articles dans une collection. (Dans ce cas, en utilisant l'opérateur C++ = = pour les doubles produit des résultats corrects.)

Comme je l'ai dit: tout dépend du contexte et de la taille attendue de a et b .

BTW, std::numeric_limits<double>::epsilon() est la"machine epsilon". C'est la différence entre 1,0 et la valeur suivante que l'on peut représenter par un double. Je suppose qu'il pourrait être utilisé dans la fonction comparer, mais seulement si les valeurs attendues sont inférieures à 1. (Ceci en réponse à la réponse de @cdv...)

aussi, si vous avez fondamentalement int arithmétique dans doubles (ici, nous utilisons des doubles pour tenir des valeurs int dans certains cas) votre arithmétique sera correcte. Par exemple 4.0/2.0 sera le même que 1.0+1.0. Ceci est aussi longtemps que vous ne faites pas des choses qui résultent en fractions (4.0/3.0) ou ne vont pas en dehors de la taille d'un int.

393
répondu Andrew Stein 2016-07-19 19:33:38

la comparaison avec une valeur epsilon est ce que la plupart des gens font (même dans la programmation de jeu).

vous devriez changer votre implémentation un peu cependant:

bool AreSame(double a, double b)
{
    return fabs(a - b) < EPSILON;
}

Edit: Christer a ajouté une pile de grande infos sur ce sujet sur un blog . Profiter.

164
répondu OJ. 2015-11-20 02:59:35

j'ai trouvé que le Google c++ Testing Framework contient une belle implémentation multi-plateforme de Almostequal2scomplément qui fonctionne à la fois sur les doubles et les flotteurs. Étant donné qu'il est publié sous la licence BSD, son utilisation dans votre propre code ne devrait poser aucun problème, tant que vous conservez la licence. J'ai extrait le code ci-dessous de http://code.google.com/p/googletest/source/browse/trunk/include/gtest/internal/gtest-internal.h https://github.com/google/googletest/blob/master/googletest/include/gtest/internal/gtest-internal.h et a ajouté la licence dessus.

assurez - vous de #définir GTEST_OS_WINDOWS à une certaine valeur (ou de changer le code où il est utilisé à quelque chose qui correspond à votre codebase-C'est BSD autorisé après tout).

Usage exemple:

double left  = // something
double right = // something
const FloatingPoint<double> lhs(left), rhs(right);

if (lhs.AlmostEquals(rhs)) {
  //they're equal!
}

voici le code:

// Copyright 2005, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Authors: wan@google.com (Zhanyong Wan), eefacm@gmail.com (Sean Mcafee)
//
// The Google C++ Testing Framework (Google Test)


// This template class serves as a compile-time function from size to
// type.  It maps a size in bytes to a primitive type with that
// size. e.g.
//
//   TypeWithSize<4>::UInt
//
// is typedef-ed to be unsigned int (unsigned integer made up of 4
// bytes).
//
// Such functionality should belong to STL, but I cannot find it
// there.
//
// Google Test uses this class in the implementation of floating-point
// comparison.
//
// For now it only handles UInt (unsigned int) as that's all Google Test
// needs.  Other types can be easily added in the future if need
// arises.
template <size_t size>
class TypeWithSize {
 public:
  // This prevents the user from using TypeWithSize<N> with incorrect
  // values of N.
  typedef void UInt;
};

// The specialization for size 4.
template <>
class TypeWithSize<4> {
 public:
  // unsigned int has size 4 in both gcc and MSVC.
  //
  // As base/basictypes.h doesn't compile on Windows, we cannot use
  // uint32, uint64, and etc here.
  typedef int Int;
  typedef unsigned int UInt;
};

// The specialization for size 8.
template <>
class TypeWithSize<8> {
 public:
#if GTEST_OS_WINDOWS
  typedef __int64 Int;
  typedef unsigned __int64 UInt;
#else
  typedef long long Int;  // NOLINT
  typedef unsigned long long UInt;  // NOLINT
#endif  // GTEST_OS_WINDOWS
};


// This template class represents an IEEE floating-point number
// (either single-precision or double-precision, depending on the
// template parameters).
//
// The purpose of this class is to do more sophisticated number
// comparison.  (Due to round-off error, etc, it's very unlikely that
// two floating-points will be equal exactly.  Hence a naive
// comparison by the == operation often doesn't work.)
//
// Format of IEEE floating-point:
//
//   The most-significant bit being the leftmost, an IEEE
//   floating-point looks like
//
//     sign_bit exponent_bits fraction_bits
//
//   Here, sign_bit is a single bit that designates the sign of the
//   number.
//
//   For float, there are 8 exponent bits and 23 fraction bits.
//
//   For double, there are 11 exponent bits and 52 fraction bits.
//
//   More details can be found at
//   http://en.wikipedia.org/wiki/IEEE_floating-point_standard.
//
// Template parameter:
//
//   RawType: the raw floating-point type (either float or double)
template <typename RawType>
class FloatingPoint {
 public:
  // Defines the unsigned integer type that has the same size as the
  // floating point number.
  typedef typename TypeWithSize<sizeof(RawType)>::UInt Bits;

  // Constants.

  // # of bits in a number.
  static const size_t kBitCount = 8*sizeof(RawType);

  // # of fraction bits in a number.
  static const size_t kFractionBitCount =
    std::numeric_limits<RawType>::digits - 1;

  // # of exponent bits in a number.
  static const size_t kExponentBitCount = kBitCount - 1 - kFractionBitCount;

  // The mask for the sign bit.
  static const Bits kSignBitMask = static_cast<Bits>(1) << (kBitCount - 1);

  // The mask for the fraction bits.
  static const Bits kFractionBitMask =
    ~static_cast<Bits>(0) >> (kExponentBitCount + 1);

  // The mask for the exponent bits.
  static const Bits kExponentBitMask = ~(kSignBitMask | kFractionBitMask);

  // How many ULP's (Units in the Last Place) we want to tolerate when
  // comparing two numbers.  The larger the value, the more error we
  // allow.  A 0 value means that two numbers must be exactly the same
  // to be considered equal.
  //
  // The maximum error of a single floating-point operation is 0.5
  // units in the last place.  On Intel CPU's, all floating-point
  // calculations are done with 80-bit precision, while double has 64
  // bits.  Therefore, 4 should be enough for ordinary use.
  //
  // See the following article for more details on ULP:
  // http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm.
  static const size_t kMaxUlps = 4;

  // Constructs a FloatingPoint from a raw floating-point number.
  //
  // On an Intel CPU, passing a non-normalized NAN (Not a Number)
  // around may change its bits, although the new value is guaranteed
  // to be also a NAN.  Therefore, don't expect this constructor to
  // preserve the bits in x when x is a NAN.
  explicit FloatingPoint(const RawType& x) { u_.value_ = x; }

  // Static methods

  // Reinterprets a bit pattern as a floating-point number.
  //
  // This function is needed to test the AlmostEquals() method.
  static RawType ReinterpretBits(const Bits bits) {
    FloatingPoint fp(0);
    fp.u_.bits_ = bits;
    return fp.u_.value_;
  }

  // Returns the floating-point number that represent positive infinity.
  static RawType Infinity() {
    return ReinterpretBits(kExponentBitMask);
  }

  // Non-static methods

  // Returns the bits that represents this number.
  const Bits &bits() const { return u_.bits_; }

  // Returns the exponent bits of this number.
  Bits exponent_bits() const { return kExponentBitMask & u_.bits_; }

  // Returns the fraction bits of this number.
  Bits fraction_bits() const { return kFractionBitMask & u_.bits_; }

  // Returns the sign bit of this number.
  Bits sign_bit() const { return kSignBitMask & u_.bits_; }

  // Returns true iff this is NAN (not a number).
  bool is_nan() const {
    // It's a NAN if the exponent bits are all ones and the fraction
    // bits are not entirely zeros.
    return (exponent_bits() == kExponentBitMask) && (fraction_bits() != 0);
  }

  // Returns true iff this number is at most kMaxUlps ULP's away from
  // rhs.  In particular, this function:
  //
  //   - returns false if either number is (or both are) NAN.
  //   - treats really large numbers as almost equal to infinity.
  //   - thinks +0.0 and -0.0 are 0 DLP's apart.
  bool AlmostEquals(const FloatingPoint& rhs) const {
    // The IEEE standard says that any comparison operation involving
    // a NAN must return false.
    if (is_nan() || rhs.is_nan()) return false;

    return DistanceBetweenSignAndMagnitudeNumbers(u_.bits_, rhs.u_.bits_)
        <= kMaxUlps;
  }

 private:
  // The data type used to store the actual floating-point number.
  union FloatingPointUnion {
    RawType value_;  // The raw floating-point number.
    Bits bits_;      // The bits that represent the number.
  };

  // Converts an integer from the sign-and-magnitude representation to
  // the biased representation.  More precisely, let N be 2 to the
  // power of (kBitCount - 1), an integer x is represented by the
  // unsigned number x + N.
  //
  // For instance,
  //
  //   -N + 1 (the most negative number representable using
  //          sign-and-magnitude) is represented by 1;
  //   0      is represented by N; and
  //   N - 1  (the biggest number representable using
  //          sign-and-magnitude) is represented by 2N - 1.
  //
  // Read http://en.wikipedia.org/wiki/Signed_number_representations
  // for more details on signed number representations.
  static Bits SignAndMagnitudeToBiased(const Bits &sam) {
    if (kSignBitMask & sam) {
      // sam represents a negative number.
      return ~sam + 1;
    } else {
      // sam represents a positive number.
      return kSignBitMask | sam;
    }
  }

  // Given two numbers in the sign-and-magnitude representation,
  // returns the distance between them as an unsigned number.
  static Bits DistanceBetweenSignAndMagnitudeNumbers(const Bits &sam1,
                                                     const Bits &sam2) {
    const Bits biased1 = SignAndMagnitudeToBiased(sam1);
    const Bits biased2 = SignAndMagnitudeToBiased(sam2);
    return (biased1 >= biased2) ? (biased1 - biased2) : (biased2 - biased1);
  }

  FloatingPointUnion u_;
};

EDIT: Ce post est de 4 ans. Il est probablement encore valide, et le code est agréable, mais certaines personnes ont trouvé des améliorations. Il est préférable d'obtenir la dernière version de AlmostEquals directement à partir du code source de Google Test, et pas celui que j'ai collé ici.

103
répondu skrebbel 2018-07-04 22:13:53

comparer des nombres à virgule flottante dépend du contexte. Car même en changeant l'ordre des opérations peut produire des résultats différents, il est important de savoir comment "l'égalité" vous voulez que les chiffres.

Comparaison de nombres en virgule flottante par Bruce Dawson est un bon endroit pour commencer lors de la recherche à virgule flottante de comparaison.

les définitions suivantes sont tirées de the art of computer programming by Knuth :

bool approximatelyEqual(float a, float b, float epsilon)
{
    return fabs(a - b) <= ( (fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * epsilon);
}

bool essentiallyEqual(float a, float b, float epsilon)
{
    return fabs(a - b) <= ( (fabs(a) > fabs(b) ? fabs(b) : fabs(a)) * epsilon);
}

bool definitelyGreaterThan(float a, float b, float epsilon)
{
    return (a - b) > ( (fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * epsilon);
}

bool definitelyLessThan(float a, float b, float epsilon)
{
    return (b - a) > ( (fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * epsilon);
}

bien sûr, le choix d'epsilon dépend du contexte, et détermine l'Égalité que vous voulez que les nombres soient.

une autre méthode de comparaison des nombres à virgule flottante consiste à examiner les ULP (unités à la dernière place) des nombres. Bien que ne traitant pas spécifiquement des comparaisons, le document ce que tout informaticien devrait savoir sur les nombres à virgule flottante est une bonne ressource pour comprendre comment fonctionne floating point et quels sont les pièges, y compris ce QU'est ULP.

86
répondu mch 2016-12-15 16:13:41

Pour un plus en profondeur l'approche de lire Comparaison de nombres en virgule flottante . Voici le code extrait de ce lien:

// Usable AlmostEqual function    
bool AlmostEqual2sComplement(float A, float B, int maxUlps)    
{    
    // Make sure maxUlps is non-negative and small enough that the    
    // default NAN won't compare as equal to anything.    
    assert(maxUlps > 0 && maxUlps < 4 * 1024 * 1024);    
    int aInt = *(int*)&A;    
    // Make aInt lexicographically ordered as a twos-complement int    
    if (aInt < 0)    
        aInt = 0x80000000 - aInt;    
    // Make bInt lexicographically ordered as a twos-complement int    
    int bInt = *(int*)&B;    
    if (bInt < 0)    
        bInt = 0x80000000 - bInt;    
    int intDiff = abs(aInt - bInt);    
    if (intDiff <= maxUlps)    
        return true;    
    return false;    
}
45
répondu grom 2008-08-20 06:31:10

la façon portable d'obtenir epsilon en C++ est

#include <limits>
std::numeric_limits<double>::epsilon()

puis la fonction de comparaison devient

#include <cmath>
#include <limits>

bool AreSame(double a, double b) {
    return std::fabs(a - b) < std::numeric_limits<double>::epsilon();
}
26
répondu Chris de Vries 2010-08-08 00:44:16

réaliser que c'est un vieux fil, mais cet article est l'un des plus simples que j'ai trouvé sur la comparaison des nombres à virgule flottante et si vous voulez explorer plus il a des références plus détaillées ainsi et il le site principal couvre une gamme complète de questions traitant des nombres à virgule flottante le guide à virgule flottante :comparaison .

nous pouvons trouver un article un peu plus pratique dans tolérances en virgule flottante revisited et note qu'il existe tolérance absolue test " qui se résume à ceci en C++:

bool absoluteToleranceCompare(double x, double y)
{
    return std::fabs(x - y) <= std::numeric_limits<double>::epsilon() ;
}

et relative tolérance test:

bool relativeToleranceCompare(double x, double y)
{
    double maxXY = std::max( std::fabs(x) , std::fabs(y) ) ;
    return std::fabs(x - y) <= std::numeric_limits<double>::epsilon()*maxXY ;
}

l'article note que le test absolu échoue lorsque x et y sont grands et échoue dans le cas relatif lorsqu'ils sont petits. En supposant qu'il absolue et relative tolérance est le même test combiné ressemblerait comme ceci:

bool combinedToleranceCompare(double x, double y)
{
    double maxXYOne = std::max( { 1.0, std::fabs(x) , std::fabs(y) } ) ;

    return std::fabs(x - y) <= std::numeric_limits<double>::epsilon()*maxXYOne ;
}
23
répondu Shafik Yaghmour 2014-03-19 15:25:52

le code que vous avez écrit est truqué:

return (diff < EPSILON) && (-diff > EPSILON);

le code correct serait:

return (diff < EPSILON) && (diff > -EPSILON);

(...et oui, c'est différent)

je me demande si fabs ne vous ferait pas perdre évaluation paresseuse dans certains cas. Je dirais que cela dépend du compilateur. Vous pouvez essayer les deux. S'ils sont équivalents en moyenne, prendre la mise en œuvre avec fabs.

Si vous avez des infos sur le flotteur est plus susceptible d'être plus grande que d'autres, vous pouvez jouer sur l'ordre de la comparaison de mieux tirer parti de l'évaluation différée.

enfin vous pourriez obtenir de meilleurs résultats en maintenant cette fonction. Pas de nature à améliorer très bien...

Edit: JO, merci pour la correction de votre code. J'ai effacé mon commentaire en conséquence

14
répondu fulmicoton 2008-08-20 05:20:55

`return fabs (a - b) < EPSILON;

c'est très bien si:

  • l'ordre de grandeur de vos entrées ne change pas beaucoup
  • de très petits nombres de signes opposés peuvent être traités comme égaux

mais sinon ça va vous attirer des ennuis. Nombres Double précision ont une résolution d'environ 16 décimales. Si les deux nombres que vous comparez sont d'une magnitude supérieure à EPSILON*1.0E16, alors autant dire:

return a==b;

je vais examiner une approche différente qui suppose que vous devez vous soucier de la première question et de supposer que la deuxième est d'accord votre application. Une solution serait quelque chose comme:

#define VERYSMALL  (1.0E-150)
#define EPSILON    (1.0E-8)
bool AreSame(double a, double b)
{
    double absDiff = fabs(a - b);
    if (absDiff < VERYSMALL)
    {
        return true;
    }

    double maxAbs  = max(fabs(a) - fabs(b));
    return (absDiff/maxAbs) < EPSILON;
}

c'est un calcul coûteux, mais c'est parfois ce qu'il faut. C'est ce que nous avons à faire à mon entreprise parce que nous traitons avec une bibliothèque d'ingénierie et d'intrants peut varier de quelques dizaines d'ordres de grandeur.

de toute façon, le point est ceci (et s'applique à pratiquement tous les problèmes de programmation): évaluer ce que vos besoins sont, puis trouver une solution pour répondre à vos besoins -- ne présumez pas que la réponse facile répondra à vos besoins. Si après votre évaluation, vous trouvez que fabs(a-b) < EPSILON suffira, parfait -- l'utiliser! Mais être conscient de ses lacunes et d'autres solutions possibles.

14
répondu 2008-09-01 08:00:56

comme d'autres l'ont souligné, en utilisant un exposant fixe epsilon (tel que 0.0000001) sera inutile pour des valeurs éloignées de la valeur epsilon. Par exemple, si vos deux valeurs sont 10000.000977 et 10000, alors il y a NO les valeurs à virgule flottante 32 bits entre ces deux nombres -- 10000 et 10000.000977 sont aussi proches que vous pouvez l'obtenir sans être un peu identique. Ici, un epsilon de moins de 0.0009 est dénué de sens; vous pourriez aussi bien utiliser l'opérateur d'égalité droite.

de même, comme les deux valeurs approchent epsilon en taille, l'erreur relative augmente à 100%.

ainsi, essayer de mélanger un nombre de point fixe tel que 0.00001 avec des valeurs de point flottant (où l'exposant est arbitraire) est un exercice inutile. Cela ne fonctionnera jamais si vous pouvez être assuré que l'opérande valeurs se trouvent dans un étroit domaine (qui est, à proximité de certains exposant), et si vous sélectionner correctement une valeur epsilon pour le test. Si vous retirez un nombre de l'air ("Hey! 0,00001 est petit, donc ça doit être bon!"), vous êtes condamnés aux erreurs numériques. J'ai passé beaucoup de temps à déboguer du mauvais code numérique où un pauvre crétin jette au hasard des valeurs epsilon pour faire encore un autre cas de test.

Si vous ne programmation numériques de toute sorte et de croire que vous devez atteindre pour les points fixes d'epsilons, LIRE BRUCE ARTICLE SUR la COMPARAISON des données à virgule FLOTTANTE Numéros .

Comparaison De Nombres En Virgule Flottante

7
répondu Steve Hollasch 2011-09-12 22:48:55

j'ai fini par passer un certain temps à parcourir le matériel de ce grand fil. Je doute que tout le monde veuille passer autant de temps donc je voudrais souligner le résumé de ce que j'ai appris et la solution que j'ai mis en œuvre.

Résumé

  1. il y a deux problèmes avec les comparaisons de flotteurs: vous avez une précision limitée et la signification de" approximativement zéro " dépend du contexte (voir point suivant).
  2. est-ce que 1E-8 est à peu près identique à 1E-16? Si vous regardez des données de capteurs bruyants alors probablement oui, mais si vous faites la simulation moléculaire alors peut-être pas! Ligne du bas: vous devez toujours penser à tolérance valeur dans le contexte de l'appel de fonction spécifique et pas seulement le rendre Générique app-wide constante codée en dur.
  3. pour les fonctions générales de la bibliothèque, il est toujours agréable d'avoir un paramètre avec tolérance par défaut . Un le choix type est numeric_limits::epsilon() qui est le même que FLT_EPSILON dans float.H. Cela est toutefois problématique parce que epsilon pour comparer des valeurs comme 1.0 si ce n'est pas la même chose que epsilon pour des valeurs comme 1E9. Le FLT_EPSILON est défini pour 1.0.
  4. l'implémentation évidente pour vérifier si le nombre est à l'intérieur de la tolérance est fabs(a-b) <= epsilon cependant cela ne fonctionne pas car epsilon par défaut est défini pour 1.0. Nous devons augmenter ou diminuer l'échelle d'epsilon en termes de a et B.
  5. là sont deux solutions à ce problème: soit vous définissez epsilon proportionnel à max(a,b) ou vous pouvez obtenir les prochains nombres représentables autour de a et ensuite voir si b tombe dans cette gamme. Le premier est appelé méthode "relative" et plus tard est appelé méthode ULP.
  6. les deux méthodes échouent de toute façon en comparant avec 0. Dans ce cas, l'application doit fournir la tolérance correcte.

Fonctions De L'Utilitaire De Mise En Œuvre (C++11)

//implements relative method - do not use for comparing with zero
//use this most of the time, tolerance needs to be meaningful in your context
template<typename TReal>
static bool isApproximatelyEqual(TReal a, TReal b, TReal tolerance = std::numeric_limits<TReal>::epsilon())
{
    TReal diff = std::fabs(a - b);
    if (diff <= tolerance)
        return true;

    if (diff < std::fmax(std::fabs(a), std::fabs(b)) * tolerance)
        return true;

    return false;
}

//supply tolerance that is meaningful in your context
//for example, default tolerance may not work if you are comparing double with float
template<typename TReal>
static bool isApproximatelyZero(TReal a, TReal tolerance = std::numeric_limits<TReal>::epsilon())
{
    if (std::fabs(a) <= tolerance)
        return true;
    return false;
}


//use this when you want to be on safe side
//for example, don't start rover unless signal is above 1
template<typename TReal>
static bool isDefinitelyLessThan(TReal a, TReal b, TReal tolerance = std::numeric_limits<TReal>::epsilon())
{
    TReal diff = a - b;
    if (diff < tolerance)
        return true;

    if (diff < std::fmax(std::fabs(a), std::fabs(b)) * tolerance)
        return true;

    return false;
}
template<typename TReal>
static bool isDefinitelyGreaterThan(TReal a, TReal b, TReal tolerance = std::numeric_limits<TReal>::epsilon())
{
    TReal diff = a - b;
    if (diff > tolerance)
        return true;

    if (diff > std::fmax(std::fabs(a), std::fabs(b)) * tolerance)
        return true;

    return false;
}

//implements ULP method
//use this when you are only concerned about floating point precision issue
//for example, if you want to see if a is 1.0 by checking if its within
//10 closest representable floating point numbers around 1.0.
template<typename TReal>
static bool isWithinPrecisionInterval(TReal a, TReal b, unsigned int interval_size = 1)
{
    TReal min_a = a - (a - std::nextafter(a, std::numeric_limits<TReal>::lowest())) * interval_size;
    TReal max_a = a + (std::nextafter(a, std::numeric_limits<TReal>::max()) - a) * interval_size;

    return min_a <= b && max_a >= b;
}
7
répondu ShitalShah 2017-05-15 23:07:35

à des fins Générales de la comparaison de nombres à virgule flottante est généralement vide de sens. Comment comparer dépend vraiment un problème à portée de main. Dans beaucoup de problèmes, les nombres sont suffisamment discrétisés pour permettre de les comparer dans une tolérance donnée. Malheureusement, il y a autant de problèmes, où une telle ruse ne fonctionne pas vraiment. Par exemple, envisagez de travailler avec une fonction Heaviside (step) d'un nombre en question (digital stock options viennent à l'esprit) lorsque vos observations sont très près de la barrière. La comparaison fondée sur la tolérance ne ferait pas grand-chose, car elle ferait passer la question de la barrière initiale à deux nouvelles barrières. Encore une fois, il n'y a pas de solution universelle pour ces problèmes et la solution particulière pourrait exiger d'aller jusqu'à changer la méthode numérique afin d'atteindre la stabilité.

3
répondu 2008-08-28 11:55:52

malheureusement, même votre code" wasteful " est incorrect. EPSILON est la plus petite valeur qui pourrait être ajoutée à 1.0 et changer sa valeur. La valeur 1.0 est très importante - les nombres plus grands ne changent pas lorsqu'ils sont ajoutés à EPSILON. Maintenant, vous pouvez mettre cette valeur à l'échelle des nombres que vous comparez pour dire s'ils sont différents ou non. L'expression correcte pour comparer deux doubles est:

if (fabs(a - b) <= DBL_EPSILON * fmax(fabs(a), fabs(b)))
{
    // ...
}

C'est à minimum. En général, cependant, vous voudriez tenir compte du bruit dans vos calculs et ignorer quelques-uns des bits les moins significatifs, ainsi une comparaison plus réaliste ressemblerait à:

if (fabs(a - b) <= 16 * DBL_EPSILON * fmax(fabs(a), fabs(b)))
{
    // ...
}

si la performance de comparaison est très importante pour vous et que vous connaissez la gamme de vos valeurs, alors vous devriez utiliser des nombres à points fixes à la place.

2
répondu Don Reba 2011-07-29 13:55:47

Ma classe déjà posté des réponses. Très similaire au code de Google, mais j'utilise un biais qui pousse toutes les valeurs de NaN au-dessus de 0xFF000000. Cela permet un contrôle plus rapide pour NaN.

ce code est destiné à démontrer le concept, et non à être une solution générale. Le code de Google montre déjà comment calculer toutes les valeurs spécifiques de la plate-forme et je ne voulais pas dupliquer tout cela. J'ai fait peu de tests sur ce code.

typedef unsigned int   U32;
//  Float           Memory          Bias (unsigned)
//  -----           ------          ---------------
//   NaN            0xFFFFFFFF      0xFF800001
//   NaN            0xFF800001      0xFFFFFFFF
//  -Infinity       0xFF800000      0x00000000 ---
//  -3.40282e+038   0xFF7FFFFF      0x00000001    |
//  -1.40130e-045   0x80000001      0x7F7FFFFF    |
//  -0.0            0x80000000      0x7F800000    |--- Valid <= 0xFF000000.
//   0.0            0x00000000      0x7F800000    |    NaN > 0xFF000000
//   1.40130e-045   0x00000001      0x7F800001    |
//   3.40282e+038   0x7F7FFFFF      0xFEFFFFFF    |
//   Infinity       0x7F800000      0xFF000000 ---
//   NaN            0x7F800001      0xFF000001
//   NaN            0x7FFFFFFF      0xFF7FFFFF
//
//   Either value of NaN returns false.
//   -Infinity and +Infinity are not "close".
//   -0 and +0 are equal.
//
class CompareFloat{
public:
    union{
        float     m_f32;
        U32       m_u32;
    };
    static bool   CompareFloat::IsClose( float A, float B, U32 unitsDelta = 4 )
                  {
                      U32    a = CompareFloat::GetBiased( A );
                      U32    b = CompareFloat::GetBiased( B );

                      if ( (a > 0xFF000000) || (b > 0xFF000000) )
                      {
                          return( false );
                      }
                      return( (static_cast<U32>(abs( a - b ))) < unitsDelta );
                  }
    protected:
    static U32    CompareFloat::GetBiased( float f )
                  {
                      U32    r = ((CompareFloat*)&f)->m_u32;

                      if ( r & 0x80000000 )
                      {
                          return( ~r - 0x007FFFFF );
                      }
                      return( r + 0x7F800000 );
                  }
};
2
répondu WaterbugDesign 2012-01-26 21:01:16

cela dépend de la précision de la comparaison. Si vous voulez comparer, pour exactement le même nombre, alors allez simplement avec ==. (Vous ne voulez presque jamais faire cela à moins que vous voulez réellement le même nombre.) Sur n'importe quelle plate-forme décente vous pouvez aussi faire ce qui suit:

diff= a - b; return fabs(diff)<EPSILON;

comme fabs tend à être assez rapide. Par assez rapide je veux dire qu'il est fondamentalement un peu sage et, donc il vaudrait mieux que ce soit rapide.

et des trucs entiers pour comparer les doubles et les flotteurs est agréable, mais ont tendance à le rendre plus difficile pour les différents pipelines CPU à manipuler efficacement. Et ce n'est certainement pas plus rapide sur certaines architectures en ordre de nos jours en raison de l'utilisation de la pile comme zone de stockage temporaire pour des valeurs qui sont utilisées fréquemment. (Charge-hit-store pour ceux qui en prennent soin.)

1
répondu Mat Noguchi 2012-07-03 14:49:47

Qt met en œuvre deux fonctions, que vous pouvez apprendre d'eux:

static inline bool qFuzzyCompare(double p1, double p2)
{
    return (qAbs(p1 - p2) <= 0.000000000001 * qMin(qAbs(p1), qAbs(p2)));
}

static inline bool qFuzzyCompare(float p1, float p2)
{
    return (qAbs(p1 - p2) <= 0.00001f * qMin(qAbs(p1), qAbs(p2)));
}

Et vous pouvez avoir besoin des fonctions suivantes, depuis

noter que la comparaison des valeurs où p1 ou p2 est 0,0 ne fonctionnera pas, ni la comparaison des valeurs où l'une des valeurs est NaN ou l'infini. Si l'une des valeurs est toujours 0.0, utilisez qFuzzyIsNull à la place. Si l'on des valeurs est susceptible d'être 0.0, une solution est d'ajouter 1,0 à la fois valeur.

static inline bool qFuzzyIsNull(double d)
{
    return qAbs(d) <= 0.000000000001;
}

static inline bool qFuzzyIsNull(float f)
{
    return qAbs(f) <= 0.00001f;
}
1
répondu Dana Yan 2018-06-25 03:30:10

je me méfierais beaucoup de toutes ces réponses qui impliquent une soustraction de virgule flottante (par exemple, fabs(a-b) < epsilon). Tout d'abord, les nombres de points flottants deviennent plus épars à des grandeurs plus grandes et à des grandeurs suffisamment élevées où l'espacement est plus grand qu'epsilon, vous pourriez aussi bien faire simplement a == B. Deuxièmement, soustraire deux nombres flottants très proches (comme ceux-ci auront tendance à être, étant donné que vous cherchez l'égalité proche) est exactement comment vous obtenez catastrophique annulation .

bien que non portable, je pense que la réponse de grom fait le meilleur travail d'éviter ces problèmes.

0
répondu Boojum 2010-08-08 00:59:20

il y a effectivement des cas dans les logiciels numériques où vous voulez vérifier si deux nombres à virgule flottante sont exactement égaux. J'ai posté ceci sur une question similaire

https://stackoverflow.com/a/10973098/1447411

donc vous ne pouvez pas dire que" CompareDoubles1 " est erroné en général.

0
répondu Michael Lehn 2017-05-23 12:18:30

en termes d'échelle des quantités:

si epsilon est la petite fraction de la grandeur de la quantité (c.-à-d. la valeur relative) dans un certain sens physique et A et B types est comparable dans le même sens, que je pense, que le suivant est tout à fait correct:

#include <limits>
#include <iomanip>
#include <iostream>

#include <cmath>
#include <cstdlib>
#include <cassert>

template< typename A, typename B >
inline
bool close_enough(A const & a, B const & b,
                  typename std::common_type< A, B >::type const & epsilon)
{
    using std::isless;
    assert(isless(0, epsilon)); // epsilon is a part of the whole quantity
    assert(isless(epsilon, 1));
    using std::abs;
    auto const delta = abs(a - b);
    auto const x = abs(a);
    auto const y = abs(b);
    // comparable generally and |a - b| < eps * (|a| + |b|) / 2
    return isless(epsilon * y, x) && isless(epsilon * x, y) && isless((delta + delta) / (x + y), epsilon);
}

int main()
{
    std::cout << std::boolalpha << close_enough(0.9, 1.0, 0.1) << std::endl;
    std::cout << std::boolalpha << close_enough(1.0, 1.1, 0.1) << std::endl;
    std::cout << std::boolalpha << close_enough(1.1,    1.2,    0.01) << std::endl;
    std::cout << std::boolalpha << close_enough(1.0001, 1.0002, 0.01) << std::endl;
    std::cout << std::boolalpha << close_enough(1.0, 0.01, 0.1) << std::endl;
    return EXIT_SUCCESS;
}
0
répondu Orient 2013-08-29 18:33:55

j'écris ceci pour java, mais peut-être que vous le trouvez utile. Il utilise longs au lieu de doubles, mais prend soin de NaNs, sous-normaux, etc.

public static boolean equal(double a, double b) {
    final long fm = 0xFFFFFFFFFFFFFL;       // fraction mask
    final long sm = 0x8000000000000000L;    // sign mask
    final long cm = 0x8000000000000L;       // most significant decimal bit mask
    long c = Double.doubleToLongBits(a), d = Double.doubleToLongBits(b);        
    int ea = (int) (c >> 52 & 2047), eb = (int) (d >> 52 & 2047);
    if (ea == 2047 && (c & fm) != 0 || eb == 2047 && (d & fm) != 0) return false;   // NaN 
    if (c == d) return true;                            // identical - fast check
    if (ea == 0 && eb == 0) return true;                // ±0 or subnormals
    if ((c & sm) != (d & sm)) return false;             // different signs
    if (abs(ea - eb) > 1) return false;                 // b > 2*a or a > 2*b
    d <<= 12; c <<= 12;
    if (ea < eb) c = c >> 1 | sm;
    else if (ea > eb) d = d >> 1 | sm;
    c -= d;
    return c < 65536 && c > -65536;     // don't use abs(), because:
    // There is a posibility c=0x8000000000000000 which cannot be converted to positive
}
public static boolean zero(double a) { return (Double.doubleToLongBits(a) >> 52 & 2047) < 3; }

gardez à l'esprit qu'après un certain nombre d'opérations flottantes, le nombre peut être très différent de ce que nous attendons. Il n'y a pas de code pour arranger ça.

0
répondu Chameleon 2017-11-30 16:15:33

Voici la preuve que l'utilisation de std::numeric_limits::epsilon() n'est pas la réponse - elle échoue pour des valeurs supérieures à un:

preuve de mon commentaire ci-dessus:

#include <stdio.h>
#include <limits>

double ItoD (__int64 x) {
    // Return double from 64-bit hexadecimal representation.
    return *(reinterpret_cast<double*>(&x));
}

void test (__int64 ai, __int64 bi) {
    double a = ItoD(ai), b = ItoD(bi);
    bool close = std::fabs(a-b) < std::numeric_limits<double>::epsilon();
    printf ("%.16f and %.16f %s close.\n", a, b, close ? "are " : "are not");
}

int main()
{
    test (0x3fe0000000000000L,
          0x3fe0000000000001L);

    test (0x3ff0000000000000L,
          0x3ff0000000000001L);
}

Exécution des rendements de cette sortie:

0.5000000000000000 and 0.5000000000000001 are  close.
1.0000000000000000 and 1.0000000000000002 are not close.

Notez que dans le deuxième cas (et juste plus d'un), les deux valeurs d'entrée sont aussi proches qu'elles peuvent l'être, et encore les comparer de ne pas fermer. Ainsi, pour des valeurs supérieures à 1,0, vous pourriez tout aussi bien utiliser un test d'égalité. Les epsilons fixes ne vous sauveront pas lorsque vous comparez des valeurs à virgule flottante.

0
répondu Steve Hollasch 2018-06-23 02:30:51
/// testing whether two doubles are almost equal. We consider two doubles
/// equal if the difference is within the range [0, epsilon).
///
/// epsilon: a positive number (supposed to be small)
///
/// if either x or y is 0, then we are comparing the absolute difference to
/// epsilon.
/// if both x and y are non-zero, then we are comparing the relative difference
/// to epsilon.
bool almost_equal(double x, double y, double epsilon)
{
    double diff = x - y;
    if (x != 0 && y != 0){
        diff = diff/y; 
    }

    if (diff < epsilon && -1.0*diff < epsilon){
        return true;
    }
    return false;
}

j'ai utilisé cette fonction pour mon petit projet et cela fonctionne, mais notez ce qui suit:

double erreur de précision peut créer une surprise pour vous. Disons que epsilon = 1.0 e-6, puis 1.0 et 1.000001 ne devraient pas être considérés comme égaux selon le code ci-dessus, mais sur ma machine la fonction les considère comme égaux, c'est parce que 1.000001 ne peut pas être précisément traduit à un format binaire, il est probablement 1.0000009 xxx. Je le teste avec 1.0 et 1.0000011 et cette fois, j'obtiens le résultat escompté.

-1
répondu Murphy78 2014-03-04 03:06:48

j'utilise ce code:

bool AlmostEqual(double v1, double v2)
    {
        return (std::fabs(v1 - v2) < std::fabs(std::min(v1, v2)) * std::numeric_limits<double>::epsilon());
    }
-1
répondu Chunde Huang 2017-05-26 22:11:42

à Ma façon ne peut pas être correcte, mais utile

Convertissez les deux flotteurs en cordes et faites la comparaison de cordes

bool IsFlaotEqual(float a, float b, int decimal)
{
    TCHAR form[50] = _T("");
    _stprintf(form, _T("%%.%df"), decimal);


    TCHAR a1[30] = _T(""), a2[30] = _T("");
    _stprintf(a1, form, a);
    _stprintf(a2, form, b);

    if( _tcscmp(a1, a2) == 0 )
        return true;

    return false;

}

le chevauchement de l'opérateur peut aussi être fait

-2
répondu Vijay 2013-12-03 08:03:32

vous ne pouvez pas comparer deux double avec un EPSILON fixe . Selon la valeur de double , EPSILON varie.

une meilleure double comparaison serait:

bool same(double a, double b)
{
  return std::nextafter(a, std::numeric_limits<double>::lowest()) <= b
    && std::nextafter(a, std::numeric_limits<double>::max()) >= b;
}
-2
répondu Daniel Laügt 2016-02-06 18:07:53

de manière plus générique:

template <typename T>
bool compareNumber(const T& a, const T& b) {
    return std::abs(a - b) < std::numeric_limits<T>::epsilon();
}
-2
répondu berosoboy 2016-09-16 03:54:54

pourquoi ne pas exécuter bitwise XOR? Deux nombres à virgule flottante sont égales si leurs bits correspondants sont égaux. Je pense, la décision de placer les bits exposant avant mantissa a été faite pour accélérer la comparaison de deux flotteurs. Je pense que beaucoup de réponses ici manquent le point de comparaison d'epsilon. La valeur d'Epsilon dépend uniquement de la précision avec laquelle les nombres à virgule flottante sont comparés. Par exemple, après avoir fait de l'arithmétique avec des flotteurs vous obtenez deux numéros: 2.5642943554342 et 2.5642943554345. Ils ne sont pas égaux, mais pour la solution seulement 3 chiffres décimaux de matière alors ils sont égaux: 2.564 et 2.564. Dans ce cas, vous choisissez epsilon égal à 0,001. La comparaison Epsilon est également possible avec bitwise XOR. Corrigez-moi si je me trompe.

-2
répondu RadioTransmission 2016-11-13 22:31:33