Y a-t-il une fonction de signe standard (signum, sgn) en C/C++?
je veux une fonction qui retourne -1 pour les nombres négatifs et +1 pour les nombres positifs. http://en.wikipedia.org/wiki/Sign_function c/" class="blnk">C'est assez facile d'écrire le mien, mais ça ressemble à quelque chose qui devrait être dans une bibliothèque standard quelque part.
Edit: plus précisément, je cherchais une fonction travaillant sur flotteurs.
23 réponses
surpris que personne n'ait encore posté la version C++ sans branchement et sans danger:
template <typename T> int sgn(T val) {
return (T(0) < val) - (val < T(0));
}
prestations:
- implémente effectivement signum (-1, 0, ou 1). Les implémentations utilisant ici copysign ne renvoient que -1 ou 1, ce qui n'est pas signum. En outre, certaines implémentations ici renvoient un flottant (ou T) plutôt qu'un int, ce qui semble un gaspillage.
- fonctionne pour ints, flotteurs, doubles, shorts non signés, ou tout type personnalisé constructible à partir de entier 0 et peut être commandé.
- vite!
copysign
est lent, surtout si vous avez besoin de promouvoir et puis affinez à nouveau. C'est sans branchement et optimise excellemment - conforme aux normes! Le hack bitshift est soigné, mais ne fonctionne que pour quelques représentations de bits, et ne fonctionne pas quand vous avez un type non signé. Elle pourrait être fournie à titre de spécialisation manuelle, le cas échéant.
- exact! Des comparaisons simples avec zéro peut maintenir la représentation interne de haute précision de la machine (par exemple 80 bits sur x87), et éviter un rond prématuré à zéro.
mises en garde:
- C'est un modèle donc ça prendra une éternité à compiler.
- apparemment, certaines personnes pensent que l'utilisation d'une nouvelle, quelque peu ésotérique, et très lente fonction de bibliothèque standard qui ne met même pas vraiment en œuvre signum est plus compréhensible.
-
la partie
< 0
du contrôle déclenche L'avertissement-Wtype-limits
de GCC lorsqu'elle est instanciée pour un type non signé. Vous pouvez éviter cela en utilisant quelques 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>()); }
(Qui est un bon exemple de la première mise en garde.)
Je ne connais pas de fonction standard pour cela. Voici 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 ternary, vous pouvez le faire:
(x > 0) ? 1 : ((x < 0) ? -1 : 0)
il existe une fonction de bibliothèque de mathématiques 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, en fonction du signe de valeur. Notez que les Zéros à virgule flottante sont signés: (+0) donnera +1, et (-0) donnera -1.
apparemment, la réponse à la question de l'affiche originale est non. Il n'y a pas de fonction standard C++ sgn
.
il semble que la plupart des réponses n'aient pas répondu à la question initiale.
y a-t-il une fonction de signal standard (signum, sgn) en C/C++?
pas dans la bibliothèque standard, mais il y a en boost
, qui pourrait 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);
plus rapide que les solutions ci-dessus, y compris la solution la mieux notée:
(x < 0) ? -1 : (x > 0)
il y a un moyen de le faire sans ramification, mais ce n'est pas très joli.
sign = -(int)((unsigned int)((int)v) >> (sizeof(int) * CHAR_BIT - 1));
http://graphics.stanford.edu/~seander/bithacks.html
beaucoup d'autres choses intéressantes, trop intelligentes sur cette page, aussi...
y a-t-il une fonction de signe standard (signum, sgn) en C/C++?
Oui, selon la définition.
C99 et plus tard a la signbit()
macro <math.h>
int signbit
(réel-flottantx
);
La macrosignbit
renvoie une valeur non nulle si et seulement si le signe de sa valeur d'argument est négatif. C11 §7.12.3.6
pourtant OP veut quelque chose d'un peu différent.
je veux une fonction qui retourne -1 pour les nombres négatifs et +1 pour les nombres positifs. ... une fonction travaillant sur flotteurs.
#define signbit_p1_or_n1(x) ((signbit(x) ? -1 : 1)
plus profond:
Le poste n'est pas spécifique dans les cas suivants, x = 0.0, -0.0, +NaN, -NaN
.
Un classique signum()
renvoie +1
sur x>0
, -1
sur x>0
et 0
sur x==0
.
beaucoup de réponses ont déjà couvert cela, mais n'abordent pas x = -0.0, +NaN, -NaN
. Beaucoup sont conçus pour un point de vue entier qui manque généralement pas-a-Nombres ( NaN ) et -0.0 .
réponses typiques fonction comme signnum_typical()
sur -0.0, +NaN, -NaN
, ils renvoient 0.0, 0.0, 0.0
.
int signnum_typical(double x) {
if (x > 0.0) return 1;
if (x < 0.0) return -1;
return 0;
}
au lieu de cela, proposer cette fonctionnalité: sur -0.0, +NaN, -NaN
, il retourne -0.0, +NaN, -NaN
.
double signnum_c(double x) {
if (x > 0.0) return 1.0;
if (x < 0.0) return -1.0;
return x;
}
si tout ce que vous voulez est de tester le signe, utilisez signbit (renvoie true si son argument a un signe négatif). Je ne sais pas pourquoi vous voulez que -1 ou +1 soit retourné; copysign est plus pratique pour cela, mais il semble qu'il retournera +1 pour zéro négatif sur certaines plates-formes avec seulement un support partiel pour le zéro négatif, où signbit retournerait probablement vrai.
en général, il n'y a pas de fonction signum standard en C/C++, et l'absence d'une telle fonction fondamentale en dit long sur ces langues.
en dehors de cela, je crois que les deux points de vue de la majorité sur la bonne approche pour définir une telle fonction sont en quelque sorte corrects, et la "controverse" à ce sujet est en fait un non-argument une fois que vous prenez en compte deux mises en garde importantes:
-
Un signum fonction doit toujours retourner le type de son opérande, similaire à une fonction
abs()
, parce que signe est habituellement utilisé pour la multiplication avec une valeur absolue après que cette dernière a été traitée d'une manière ou d'une autre. Par conséquent, le cas d'utilisation principale de signum n'est pas des comparaisons mais de l'arithmétique, et cette dernière ne devrait pas impliquer de coûteuses conversions entier-à/de-virgule flottante. - Les types à virgule flottante
ne comportent pas une seule valeur exacte nulle: +0,0 peut être interprété comme" infinitésimalement au-dessus de zéro", et -0,0 comme"infinitésimalement au-dessous de zéro". C'est la raison pour laquelle les comparaisons impliquant zéro doivent être vérifiées à l'interne par rapport aux deux valeurs, et une expression comme
x == 0.0
peut être dangereuse.
en ce qui concerne C, je pense que la meilleure façon d'aller de l'avant avec les types intégraux est en effet d'utiliser l'expression (x > 0) - (x < 0)
, comme il se doit être traduit sans branche, et ne nécessite que trois opérations de base. Définissez au mieux les fonctions inline qui imposent un type de retour correspondant au type d'argument, et ajoutez un C11 define _Generic
pour associer ces fonctions à un nom commun.
avec des valeurs à virgule flottante, je pense que les fonctions en ligne basées sur C11 copysignf(1.0f, x)
, copysign(1.0, x)
, et copysignl(1.0l, x)
sont la voie à suivre, tout simplement parce qu'ils sont également très susceptibles d'être sans branche, et en outre ne nécessitent pas de casting le résultat d'entier de nouveau dans une valeur flottante de point. Vous devriez probablement commenter bien en évidence que vos implémentations de signum ne retourneront pas zéro en raison des particularités des valeurs zéro de la virgule flottante, des considérations de temps de traitement, et aussi parce qu'il est souvent très utile en arithmétique de la virgule flottante pour recevoir le signe correct -1/+1, même pour les valeurs zéro.
ma copie de C en un mot révèle l'existence d'une fonction standard appelée copysign qui pourrait être utile. Il semble que copysign(1.0, -2.0) retournerait -1.0 et copysign (1.0, 2.0) retournerait +1.0.
pas loin, hein?
Non, il n'existe pas en c++, comme dans matlab. J'utilise une macro dans mes programmes pour cela.
#define sign(a) ( ( (a) < 0 ) ? -1 : ( (a) > 0 ) )
la réponse acceptée avec la surcharge ci-dessous ne déclenche en effet pas -wType-limits mais elle déclenche -wunused-parameter sur l'argument is_signed
.
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>());
}
pour C++11 une alternative pourrait être.
template <typename T>
typename std::enable_if<std::is_unsigned<T>::value, int>::type
inline constexpr signum(T x) {
return T(0) < x;
}
template <typename T>
typename std::enable_if<std::is_signed<T>::value, int>::type
inline constexpr signum(T x) {
return (T(0) < x) - (x < T(0));
}
pour moi, il ne déclenche pas d'avertissements sur GCC 5.3.1
un peu hors-sujet, mais j'utilise ceci:
template<typename T>
constexpr int sgn(const T &a, const T &b) noexcept{
return (a > b) - (a < b);
}
template<typename T>
constexpr int sgn(const T &a) noexcept{
return sgn(a, T(0));
}
et j'ai trouvé première fonction-celle avec deux arguments, pour être beaucoup plus utile de "standard" sgn (), parce qu'il est le plus souvent utilisé dans le code comme ceci:
int comp(unsigned a, unsigned b){
return sgn( int(a) - int(b) );
}
vs.
int comp(unsigned a, unsigned b){
return sgn(a, b);
}
il n'y a pas de fonte pour les types non signés et aucun moins supplémentaire.
en fait j'ai ce morceau de code en utilisant sgn ()
template <class T>
int comp(const T &a, const T &b){
log__("all");
if (a < b)
return -1;
if (a > b)
return +1;
return 0;
}
inline int comp(int const a, int const b){
log__("int");
return a - b;
}
inline int comp(long int const a, long int const b){
log__("long");
return sgn(a, b);
}
Pourquoi utiliser des opérateurs ternaires et si-sinon quand vous pouvez simplement le faire
#define sgn(x) x==0 ? 0 : x/abs(x)
int sign(float n)
{
union { float f; std::uint32_t i; } u { n };
return 1 - ((u.i >> 31) << 1);
}
cette fonction suppose:
- binary32 la représentation des nombres en virgule flottante
- un compilateur qui fait une exception à propos de la règle d'aliasing stricte en utilisant un nommé union
#include<iostream>
#include<math.h>
using namespace std;
int main()
{
float k=10;
cout<<bool signbit(k); /* bool signbit(arg) will return "0" if arg passed is +
else "1" */
return 0;
}
le code ci-dessus peut ne pas servir votre but (obtenir 1 ou -1), mais il rend certainement beaucoup plus facile de faire ressortir le signe du type de données (int, float, double etc)
bien que la solution de nombre entier dans la réponse acceptée est assez élégante, il m'a ennuyé qu'il ne serait pas en mesure de rendre NAN pour les types doubles, donc je l'ai modifié légèrement.
template <typename T> double sgn(T val) {
return double((T(0) < val) - (val < T(0)))/(val == val);
}
notez qu'en retournant un NaN flottant par opposition à un NAN
codé en dur, le bit de signe est positionné dans quelques implémentations , de sorte que la sortie de val = -NAN
et val = NAN
va être identique quoi qu'il arrive (si vous préférez " nan
sortie " sur un -nan
vous pouvez mettre un abs(val)
avant le retour...)
vous pouvez utiliser boost::math::sign()
méthode de boost/math/special_functions/sign.hpp
si boost est disponible.
double signof(double a) { return (a == 0) ? 0 : (a<0 ? -1 : 1); }
j'ai rencontré ça aujourd'hui. Très bien, il n'y a pas de standard mais...
puisque L'OP avait juste besoin de amplifier la gamme de sortie et recentrer sur 0, (-1 à 1 pas 0 à 1) Pourquoi ne pas simplement le doubler et soustraire 1?
j'ai utilisé ceci:
(x<0)*2-1
Ou, en forçant un décalage de bits:
(x<0)< < 1-1
mais le le compilateur optimisera probablement cela de toute façon.
utiliser:
`#define sgn(x) (x<0)`
par exemple:
`if(sng(n)) { etc ....}`
ou vous pouvez utiliser un code élaboré, mais casting en premier:
inline bool sgn_long(long x)
{
return ((x<0)? true: false);
}