1
votes

Comparaison des valeurs entières absolues et comparaison des valeurs au carré. Est-ce équivalent?

Il existe le moyen standard de comparer les valeurs absolues de deux entiers:

if (a * a > b * b)
{
   // code
}

Parfois, je rencontre une autre façon de comparer les valeurs absolues en fonction de la mise au carré des valeurs:

if (std::abs(a) > std::abs(b))
{
   // code
}

Ces méthodes sont-elles équivalentes? Y a-t-il une différence dans les performances de ces méthodes? Quelle méthode préférez-vous?


9 commentaires

Non, ils ne sont pas équivalents. Les deux approches ont certains cas extrêmes (différents dans les deux cas), ce qui entraîne un comportement indéfini ou non spécifié.


J'ai également remarqué que la deuxième méthode peut provoquer un débordement. Y a-t-il des différences de performances?


Il est possible que a et b contiennent des valeurs valides, qui lorsqu'elles sont mises au carré, produisent des valeurs qui ne sont plus valides (débordement).


Sur les processeurs modernes, il est peu probable qu'il y ait une différence mesurable. Vous réalisez que les processeurs modernes exécutent des millions d'opérations par seconde et sont souvent bloqués pour l'activité d'E / S, ce qui fait que les différences de performances sont complètement perdues dans le bruit, n'est-ce pas?


Il y a une différence de performance significative. La latence sqrt est 4x la latence multipliée et sqrt n'est pas entièrement pipelined. Mais vous pouvez le chronométrer sur votre application.


@SamVarshavchik Cela dépend du problème résolu. Il existe une large classe de programmes où presque tout le temps est des calculs mathématiques.


Vous serez probablement en mesure de répondre d'abord à votre propre question, simplement en essayant les deux alternatives et en chronométrant le temps que prend chacune d'entre elles, réglant ainsi le problème. Il est peu probable que vous trouviez une différence en dehors de la marge d'erreur.


@AlainMerigot, sqrt non utilisé dans ces codes, ou ai-je tort?


@DmytroDadyka Oui. Je m'en suis rendu compte plus tard. fabs et se multiplient ont des caractéristiques similaires et ne devraient pas entraîner de différence significative.


3 Réponses :


0
votes

La valeur absolue d'un nombre renvoie son opposé s'il est inférieur à 0 et lui-même s'il est supérieur à 0.

La quadrature d'un nombre vous donnera toujours un résultat positif, donc comparer des entiers au carré revient à comparer leurs valeurs absolues. Bien que cela puisse différer avec les nombres flottants.

En bref, il est préférable d'utiliser std :: abs pour la lisibilité et pour éviter d'avoir des contradictions lorsque vous travaillez avec des flottants

EDIT:

Vous pouvez utiliser cette classe Timer pour comparer le temps d'exécution. Déclarez simplement une instance de cette classe au début de la fonction que vous souhaitez tester.

#include <ctime>
#include <chrono>
class Timer
{
  private:
    std::chrono::time_point<std::chrono::steady_clock> start;
    std::chrono::time_point<std::chrono::steady_clock> end;

  public:
    Timer()
    {
        start = std::chrono::high_resolution_clock::now();
    }
    ~Timer()
    {
        end = std::chrono::high_resolution_clock::now();
        auto duration = end - start;
        std::cout << "Function execution took  : " << duration.count() << " ns" << std::endl;
    }
};
int main()
{
    Timer t;
    return 0;
}


0 commentaires

1
votes

Les deux ne sont équivalents que si les nombres sont petits . Par exemple, si vous utilisez un entier 32 bits, alors a * a débordera autour de a = 50 000, et a * a> b * b donnera des réponses complètement erronées.

Les performances n'ont absolument aucun intérêt. Vous êtes ici dans le domaine des économies de nanosecondes. Je préfère un code qui n'échoue pas sans raison sérieuse, donc comparer les valeurs absolues est ce que vous devriez faire. C'est aussi apparemment la description de votre problème.


1 commentaires

Je suis complètement d'accord avec toi. Je travaille avec du code dans lequel se trouve souvent la deuxième variante. Mais je pense que l'auteur a juste peur de stl) Je voudrais changer tous ces fragments en première option, mais j'ai d'abord décidé de m'assurer que je comprends bien.



1
votes

Jusqu'à ce que le produit entier déborde, les deux méthodes se comportent de manière équivalente. Je préférerais utiliser std :: abs () car cela énonce plus clairement mes intentions - comparer la magnitude de deux nombres. Si j'utilise le produit a * a , tous les autres responsables qui lisent par code se demanderont ce que signifie a * a .

Bien qu'au-delà de la portée de la question littérale, je pense qu'il est important de souligner que ces deux méthodes divergent beaucoup plus rapidement pour les types non entiers. Pour la virgule flottante, vous rencontrerez des erreurs d'arrondi très rapidement, ce qui entraînera des comparaisons légèrement différentes, ce qui peut parfois donner un résultat erroné (on peut dire que vous ne devriez pas faire de comparaisons directes en virgule flottante de toute façon, mais plutôt serrer sur une plage, mais encore).

Plus de subtilité, la comparaison des valeurs complexes de cette manière sera incorrecte. Le calcul typique de la valeur absolue d'un nombre complexe a + bi est sqrt (a ^ 2 + b ^ 2) ^ dénote l'exponentiation . Cependant, (a + bi) ^ 2 donnera a ^ 2-b ^ 2 + 2abi , qui ne peut jamais être égal pour non- zéro a et b .


1 commentaires

La remarque selon laquelle la seconde méthode peut conduire à des résultats inattendus pour des types complexes me paraît importante.