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 le mien, mais il semble que quelque chose devrait se trouver quelque part dans une bibliothèque standard.
Edit: Plus précisément, je cherchais une fonction travaillant sur les flotteurs.
x==0
. Selon IEEE 754 , le zéro négatif et le zéro positif devraient se comparer comme égaux.Réponses:
Surpris, personne n'a encore publié la version C ++ de type sécurisé:
Avantages:
copysign
est lent, surtout si vous devez promouvoir puis rétrécir à nouveau. Ceci est sans branche et optimise parfaitementMises en garde:
La
< 0
partie de la vérification déclenche l'-Wtype-limits
avertissement de GCC lorsqu'il est instancié pour un type non signé. Vous pouvez éviter cela en utilisant certaines surcharges:(Ce qui est un bon exemple de la première mise en garde.)
la source
std::copysign
semble me donner un excellent code: 4 instructions (en ligne), sans branchement, utilisant entièrement le FPU. La recette donnée dans cette réponse, en revanche, génère un code bien pire (beaucoup plus d'instructions, y compris une multiplication, un va-et-vient entre l'unité entière et le FPU) ...copysign
un int, il promeut de flotter / doubler et doit se resserrer à nouveau. Votre compilateur peut optimiser cette promotion, mais je ne trouve rien suggérant que cela soit garanti par la norme. De plus, pour implémenter signum via copysign, vous devez gérer manuellement le cas 0 - assurez-vous de l'inclure dans toute comparaison de performances.Je ne connais pas de fonction standard pour cela. Voici une façon intéressante de l'écrire:
Voici une façon plus lisible de le faire:
Si vous aimez l'opérateur ternaire, vous pouvez le faire:
la source
x==0
.<
,>
... doit donner 1 si la relation spécifiée est vraie et 0 si elle est fausse"0
est "false"; toute autre valeur est "vraie"; cependant, les opérateurs relationnels et d'égalité renvoient toujours0
ou1
(voir Normes 6.5.8 et 6.5.9). - la valeur de l'expressiona * (x == 42)
est soit0
oua
.copysign
intégralex
même si je l'avais disponible.Il existe une fonction de bibliothèque mathématique C99 appelée copysign (), qui prend le signe d'un argument et la valeur absolue de l'autre:
vous donnera un résultat de +/- 1.0, selon le signe de la valeur. Notez que les zéros à virgule flottante sont signés: (+0) donnera +1 et (-0) donnera -1.
la source
Il semble que la plupart des réponses aient manqué la question initiale.
Pas dans la bibliothèque standard, mais il en existe
copysign
qui peuvent être utilisés presque de la même manière viacopysign(1.0, arg)
et il existe une véritable fonction de signe dansboost
, qui pourrait tout aussi bien faire partie de la norme.http://www.boost.org/doc/libs/1_47_0/libs/math/doc/sf_and_dist/html/math_toolkit/utils/sign_functions.html
la source
Apparemment, la réponse à la question de l'affiche originale est non. Il n'y a pas de fonction C ++ standard
sgn
.la source
copysign()
ne fera pas votre premier paramètre 0.0 si le second est 0.0. En d'autres termes, John a raison.Oui, selon la définition.
C99 et versions ultérieures ont la
signbit()
macro dans<math.h>
Pourtant, OP veut quelque chose d'un peu différent.
Plus profond:
Le poste n'est pas spécifique dans les cas suivants:
x = 0.0, -0.0, +NaN, -NaN
.Un classique des
signum()
retours+1
surx>0
,-1
surx<0
et0
surx==0
.De nombreuses réponses ont déjà couvert ce point, mais n'en traitent pas
x = -0.0, +NaN, -NaN
. Beaucoup sont adaptés à un point de vue entier qui manque généralement de Not-a-Numbers ( NaN ) et -0.0 .Les réponses typiques fonctionnent comme
signnum_typical()
On-0.0, +NaN, -NaN
, elles reviennent0.0, 0.0, 0.0
.Au lieu de cela, je propose cette fonctionnalité: Oui
-0.0, +NaN, -NaN
, elle revient-0.0, +NaN, -NaN
.la source
Plus rapide que les solutions ci-dessus, y compris la meilleure note:
la source
Il y a un moyen de le faire sans ramification, mais ce n'est pas très joli.
http://graphics.stanford.edu/~seander/bithacks.html
Beaucoup d'autres choses intéressantes et trop intelligentes sur cette page aussi ...
la source
sign = (v != 0) | -(int)((unsigned int)((int)v) >> (sizeof(int) * CHAR_BIT - 1));
ousign = (v > 0) - (v < 0);
.v
c'est un type entier pas plus large que intSi tout ce que vous voulez est de tester le signe, utilisez signbit (retourne vrai si son argument a un signe négatif). Je ne sais pas pourquoi vous voudriez particulièrement que -1 ou +1 soit retourné; copysign est plus pratique pour cela, mais il semble qu'il renverra +1 pour zéro négatif sur certaines plates-formes avec seulement un support partiel pour zéro négatif, où signbit retournerait vraisemblablement vrai.
la source
if (x < 0)
.En général, il n'y a pas de fonction de signalisation standard en C / C ++, et l'absence d'une telle fonction fondamentale en dit long sur ces langages.
En dehors de cela, je pense que les deux points de vue majoritaires 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:
Une fonction signum doit toujours retourner le type de son opérande, de la même manière qu'une
abs()
fonction, car signum est généralement 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 majeur de signum n'est pas les comparaisons mais l'arithmétique, et cette dernière ne devrait pas impliquer de conversions entières en virgule flottante coûteuses.Les types à virgule flottante ne comportent pas une seule valeur zéro exacte: +0,0 peut être interprété comme "infinitésimalement supérieur à zéro" et -0,0 comme "infinitésimalement inférieur à zéro". C'est la raison pour laquelle les comparaisons impliquant zéro doivent vérifier en 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 de progresser avec les types intégraux est en effet d'utiliser l'
(x > 0) - (x < 0)
expression, car elle doit être traduite sans branchement et ne nécessite que trois opérations de base. Définissez au mieux les fonctions en ligne qui appliquent un type de retour correspondant au type d'argument et ajoutez un C11define _Generic
pour mapper ces fonctions à un nom commun.Avec les valeurs à virgule flottante, je pense que les fonctions en ligne basées sur C11
copysignf(1.0f, x)
,copysign(1.0, x)
etcopysignl(1.0l, x)
sont la voie à suivre, simplement parce qu'elles sont également très susceptibles d'être sans branche, et en outre ne nécessitent pas de transtyper le résultat de l'entier en virgule flottante valeur. Vous devriez probablement indiquer clairement que vos implémentations en virgule flottante de signum ne retourneront pas zéro en raison des particularités des valeurs nulles en virgule flottante, des considérations de temps de traitement, et aussi parce qu'il est souvent très utile dans l'arithmétique en virgule flottante de recevoir le bon -1 / + 1 signe, même pour des valeurs nulles.la source
Ma copie de C en bref révèle l'existence d'une fonction standard appelée copysign qui pourrait être utile. Il semble que copysign (1.0, -2.0) renvoie -1.0 et copysign (1.0, 2.0) renvoie +1.0.
Assez proche hein?
la source
Non, il n'existe pas en c ++, comme dans matlab. J'utilise une macro dans mes programmes pour cela.
la source
#define sign(x) (((x) > 0) - ((x) < 0))
qui est bien aussi.La réponse acceptée avec la surcharge ci-dessous ne déclenche en effet pas -Wtype-limits .
Pour C ++ 11, une alternative pourrait être.
Pour moi, cela ne déclenche aucun avertissement sur GCC 5.3.1.
la source
-Wunused-parameter
avertissement, utilisez simplement des paramètres sans nom.Peu hors sujet, mais j'utilise ceci:
et j'ai trouvé la première fonction - celle avec deux arguments, beaucoup plus utile dans sgn () "standard", car elle est le plus souvent utilisée dans du code comme celui-ci:
contre.
il n'y a pas de transtypage pour les types non signés et aucun inconvénient supplémentaire.
en fait, j'ai ce morceau de code en utilisant sgn ()
la source
La question est ancienne mais il existe désormais ce type de fonction souhaitée. J'ai ajouté un wrapper avec not, shift gauche et dec.
Vous pouvez utiliser une fonction wrapper basée sur signbit de C99 afin d'obtenir le comportement souhaité exact (voir le code ci-dessous).
NB: J'utilise l'opérande non ("!") Car la valeur de retour de signbit n'est pas spécifiée à 1 (même si les exemples laissent penser que ce serait toujours ainsi) mais vrai pour un nombre négatif:
Ensuite, je multiplie par deux avec le décalage vers la gauche ("<< 1") qui nous donnera 2 pour un nombre positif et 0 pour un nombre négatif et enfin décrémenter de 1 pour obtenir 1 et -1 pour les nombres respectivement positifs et négatifs comme demandé par OP.
la source
Bien que la solution entière dans la réponse acceptée soit assez élégante, cela m'a dérangé de ne pas pouvoir renvoyer NAN pour les types doubles, donc je l'ai légèrement modifiée.
Notez que le retour à virgule flottante NAN par opposition à une pré - programmée
NAN
cause le bit de signe à régler dans certaines implémentations , de sorte que la sortie pourval = -NAN
etval = NAN
vont être identiques , peu importe ce que (si vous préférez une «nan
» sortie sur une ,-nan
vous pouvez mettre unabs(val)
avant le retour ...)la source
Vous pouvez utiliser la
boost::math::sign()
méthode à partir du momentboost/math/special_functions/sign.hpp
où le boost est disponible.la source
Voici une implémentation adaptée aux branchements:
À moins que vos données aient des zéros comme la moitié des nombres, ici le prédicteur de branche choisira l'une des branches comme la plus courante. Les deux branches impliquent uniquement des opérations simples.
Alternativement, sur certains compilateurs et architectures CPU, une version complètement sans branche peut être plus rapide:
Cela fonctionne pour le format à virgule flottante binaire double précision IEEE 754: binaire64 .
la source
Cette fonction suppose:
la source
copysign
; si vous utilisez,static_assert
vous avez C ++ 11, et pourriez tout aussi bien l'utilisercopysign
.la source
Pourquoi utiliser des opérateurs ternaires et if-else quand vous pouvez simplement le faire
la source
x == INT_MIN
.