468 votes

Existe-t-il une fonction standard de signe (signum, sgn) en C/C++ ?

Je veux une fonction qui renvoie -1 pour les nombres négatifs et +1 pour les nombres positifs. http://en.wikipedia.org/wiki/Sign_function Il est assez facile d'écrire mon propre code, mais il semble que ce soit quelque chose qui devrait se trouver quelque part dans une bibliothèque standard.

Edit : Plus précisément, je cherchais une fonction fonctionnant sur les flottants.

21 votes

Que doit-il retourner pour 0 ?

78 votes

@Craig McQueen ; cela dépend si c'est un zéro positif ou un zéro négatif.

1 votes

J'ai remarqué que vous avez spécifié la valeur de retour comme un nombre entier. Cherchez-vous une solution qui prend des entiers ou des nombres à virgule flottante ?

580voto

La version C++ à sécurité intrinsèque :

template <typename T> int sgn(T val) {
    return (T(0) < val) - (val < T(0));
}

Avantages :

  • Implémente actuellement le signum (-1, 0, ou 1). Les implémentations utilisant copysign ne renvoient que -1 ou 1, ce qui n'est pas signum. De plus, certaines implémentations renvoient un float (ou T) plutôt qu'un int, ce qui semble être un gaspillage.
  • Fonctionne pour les ints, les floats, les doubles, les shorts non signés, ou tout type personnalisé constructible à partir de l'entier 0 et commandable.
  • Rapidement ! copysign est lent, surtout si vous devez faire de la promotion et ensuite rétrécir à nouveau. Ceci est sans branche et s'optimise parfaitement.
  • Conforme aux normes ! Le hack bitshift est bien, mais ne fonctionne que pour certaines représentations de bits, et ne fonctionne pas lorsque vous avez un type non signé. Il pourrait être fourni comme une spécialisation manuelle lorsque cela est approprié.
  • Précis ! De simples comparaisons avec zéro peuvent maintenir la représentation interne de haute précision de la machine (par exemple 80 bits sur x87), et éviter un arrondi prématuré à zéro.

Mises en garde :

  • Il s'agit d'un modèle qui peut prendre plus de temps à compiler dans certaines circonstances.

  • Apparemment, certaines personnes pensent que l'utilisation d'une nouvelle fonction de la bibliothèque standard, quelque peu ésotérique et très lente, est une bonne chose. qui n'implémente même pas vraiment le signum est plus compréhensible.

  • El < 0 la partie de la vérification déclenche le GCC -Wtype-limits avertissement lorsqu'il est instancié pour un type non signé. Vous pouvez éviter cela en utilisant certaines surcharges :

     template <typename T> inline constexpr
     int signum(T x, std::false_type is_signed) {
         return T(0) < x;
     }
    
     template <typename T> inline constexpr
     int signum(T x, std::true_type is_signed) {
         return (T(0) < x) - (x < T(0));
     }
    
     template <typename T> inline constexpr
     int signum(T x) {
         return signum(x, std::is_signed<T>());
     }

    (Ce qui est un bon exemple du premier avertissement).

4 votes

"C'est un modèle, donc ça va prendre une éternité à compiler." Huh ? Je veux dire que techniquement il n'est pas vraiment "compilé" du tout, juste passé une fois. Lorsque vous l'instanciez, il est compilé, mais pas plus lentement qu'un modèle écrit manuellement avec un code spécifique. T .

19 votes

@GMan : GCC vient tout juste (4.5) d'arrêter d'avoir un coût quadratique au nombre d'instanciations pour les fonctions template, et elles sont toujours drastiquement plus chères à analyser et à instancier que les fonctions écrites manuellement ou le préprocesseur C standard. L'éditeur de liens doit également faire plus de travail pour supprimer les instanciations dupliquées. Les modèles encouragent également les #includes-in-#includes, ce qui rend le calcul des dépendances plus long et les petits changements (souvent d'implémentation, pas d'interface) obligent à recompiler davantage de fichiers.

2 votes

@Joe : Je suis d'accord avec vous, mais je pense que vous exagérez grandement le coût ; un modèle de fonction unique n'a certainement pas de coût notable.

305voto

Mark Byers Points 318575

Je ne connais pas de fonction standard pour cela. Voici cependant une façon intéressante de l'écrire :

(x > 0) - (x < 0)

Voici une façon plus lisible de le faire :

if (x > 0) return 1;
if (x < 0) return -1;
return 0;

Si vous aimez l'opérateur ternaire, vous pouvez le faire :

(x > 0) ? 1 : ((x < 0) ? -1 : 0)

4 votes

Il serait intéressant de le mesurer par rapport à x<0?-1:1 et 1-2*(x<0).

9 votes

Mark Ransom, vos expressions donnent des résultats incorrects pour x==0 .

0 votes

Je le nomme pour le concours mensuel prix du style badge s'il y en avait.

221voto

comingstorm Points 11392

Il existe une fonction de la bibliothèque mathématique C99 appelée copysign(), qui prend le signe d'un argument et la valeur absolue de l'autre :

result = copysign(1.0, value) // double
result = copysignf(1.0, value) // float
result = copysignl(1.0, value) // long double

vous donnera un résultat de +/- 1.0, selon le signe de la valeur. Notez que les zéros en virgule flottante sont signés : (+0) donnera +1, et (-0) donnera -1.

70 votes

J'ai mis en avant celle-ci, j'ai mis en bas la réponse la plus populaire. Je suis stupéfait que la communauté SO semble préférer un hack à l'utilisation d'une fonction de la bibliothèque standard. Que les dieux de la programmation vous condamnent tous à essayer de déchiffrer les piratages utilisés par des programmeurs malins qui ne connaissent pas les standards du langage. Oui, je sais que cela va me coûter une tonne de réputation sur SO, mais je préfère me ranger du côté de comingstorm que du reste d'entre vous ...

38 votes

C'est proche, mais cela donne la mauvaise réponse pour zéro (selon l'article Wikipedia de la question au moins). Mais c'est une bonne suggestion. +1 quand même.

4 votes

Si vous voulez un entier, ou si vous voulez le résultat exact du signum pour les zéros, j'aime la réponse de Mark Byers, qui est d'une élégance diabolique ! Si vous ne vous souciez pas de ce qui précède, copysign() pourrait avoir un avantage en termes de performance, selon l'application -- si j'optimisais une boucle critique, j'essaierais les deux.

93voto

Catskul Points 3600

Il semble que la plupart des réponses ne répondent pas à la question initiale.

Existe-t-il une fonction standard de signe (signum, sgn) en C/C++ ?

Pas dans la bibliothèque standard, cependant il y a copysign qui peut être utilisé presque de la même manière via copysign(1.0, arg) et il existe une véritable fonction de signe dans boost qui pourrait tout aussi bien faire partie de la norme.

    #include <boost/math/special_functions/sign.hpp>

    //Returns 1 if x > 0, -1 if x < 0, and 0 if x is zero.
    template <class T>
    inline int sign (const T& z);

http://www.boost.org/doc/libs/1_47_0/libs/math/doc/sf_and_dist/html/math_toolkit/utils/sign_functions.html

5 votes

Cette réponse devrait être la plus votée, car elle donne la solution la plus proche de ce qui est demandé dans la question.

0 votes

Cela fait quelques minutes que je me demande pourquoi la bibliothèque standard n'a pas de fonction de signe. Elle est tellement courante -- certainement plus utilisée que la fonction gamma qui se trouve dans l'en-tête cmath.

5 votes

L'explication que je reçois souvent pour des questions similaires est "c'est assez facile à mettre en œuvre soi-même", ce qui, selon moi, n'est pas une bonne raison. Elle ne tient absolument pas compte des problèmes de normalisation, des cas limites non évidents et de l'emplacement d'un outil aussi largement utilisé.

82voto

John Points 1978

Apparemment, la réponse à la question du posteur initial est non. Il n'y a pas standard C++ sgn fonction.

4 votes

@SR_ Vous n'avez pas raison. copysign() ne rendra pas votre premier paramètre 0.0 si le second est 0.0. En d'autres termes, John a raison.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X