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 ?

40voto

chux Points 13185

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

Oui, selon la définition.

Le C99 et les versions ultérieures ont le signbit() macro dans <math.h>

int signbit (réel-flottant x ) ;
Le site signbit renvoie une valeur non nulle si et seulement si le signe de la valeur de son argument est négatif. C11 §7.12.3.6


Pourtant, OP veut quelque chose d'un peu différent.

Je veux une fonction qui renvoie -1 pour les nombres négatifs et +1 pour les nombres positifs. ... une fonction fonctionnant sur les flottants.

#define signbit_p1_or_n1(x)  ((signbit(x) ?  -1 : 1)

Plus profond :

La question du PO 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 y 0 sur x==0 .

De nombreuses réponses ont déjà couvert ce point, mais n'abordent pas les questions suivantes x = -0.0, +NaN, -NaN . Beaucoup d'entre elles sont conçues pour un point de vue entier qui ne tient pas compte des chiffres non significatifs ( NaN ) et -0.0 .

Les réponses typiques fonctionnent comme suit signnum_typical() Sur -0.0, +NaN, -NaN ils reviennent 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, je propose cette fonctionnalité : Sur -0.0, +NaN, -NaN il renvoie -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;
}

1 votes

Ah, exactement ce que je cherche. Cela vient de changer dans Pharo Smalltalk github.com/pharo-project/pharo/pull/1835 et je me demandais s'il existait une sorte de norme (IEC 60559 ou ISO 10967) dictant le comportement pour les zéros négatifs et le comportement nan... J'aime le signe javascript developer.mozilla.org/fr/US/docs/Web/JavaScript/Référence/

30voto

xnx Points 295

Plus rapide que les solutions ci-dessus, y compris la solution la mieux notée :

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

1 votes

Quel est le type de x ? Ou utilisez-vous un #define ?

3 votes

Votre type n'est pas plus rapide. Il causera un manque de cache assez souvent.

20 votes

Cache manquant ? Je ne sais pas comment. Peut-être vouliez-vous dire mauvaise prédiction de branche ?

16voto

Tim Sylvester Points 14047

Il y a un moyen de le faire sans embranchement, 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

Il y a beaucoup d'autres choses intéressantes et trop intelligentes sur cette page, aussi...

1 votes

Si j'ai bien lu le lien, cela ne renvoie que -1 ou 0. Si vous voulez -1, 0 ou +1, alors c'est sign = (v != 0) | -(int)((unsigned int)((int)v) >> (sizeof(int) * CHAR_BIT - 1)); o sign = (v > 0) - (v < 0); .

2 votes

Cela implique que v est un type d'entier pas plus large que int

13voto

ysth Points 54757

Si tout ce que vous voulez est de tester le signe, utilisez signbit (renvoie vrai si son argument a un signe négatif). Je ne vois pas pourquoi vous voudriez particulièrement que -1 ou +1 soit retourné ; copysign est plus pratique plus pratique pour cela, mais il semble qu'il retournera +1 pour un zéro négatif sur certaines plateformes avec support partiel du zéro négatif, où signbit retournerait vraisemblablement vrai.

8 votes

Il existe de nombreuses applications mathématiques dans lesquelles le signe (x) est nécessaire. Sinon, je ferais simplement if (x < 0) .

7voto

Tabernakel Points 81

En général, il n'existe pas de fonction standard de signe en C/C++, et l'absence d'une fonction aussi 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 :

  • A signum doit toujours retourner le type de son opérande, de la même manière qu'une fonction abs() car signum est généralement utilisé pour la multiplication avec une valeur absolue après que cette dernière ait été traitée d'une manière ou d'une autre. Par conséquent, le principal cas d'utilisation de signum ne sont pas des comparaisons mais de l'arithmétique, et cette dernière ne devrait pas impliquer de coûteuses conversions d'entier à virgule flottante.

  • Les types à virgule flottante ne présentent pas une seule valeur zéro exacte : +0,0 peut être interprété comme "infiniment supérieur à zéro", et -0,0 comme "infiniment inférieur à zéro". C'est la raison pour laquelle les comparaisons impliquant le zéro doivent vérifier en interne les deux valeurs, et une expression comme x == 0.0 peut être dangereux.

En ce qui concerne le C, je pense que la meilleure façon d'avancer avec les types intégraux est en effet d'utiliser la fonction (x > 0) - (x < 0) car elle doit être traduite sans branche et ne nécessite que trois opérations de base. Il est préférable de définir des fonctions en ligne qui imposent un type de retour correspondant au type d'argument et d'ajouter une fonction C11 define _Generic pour faire correspondre 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) y copysignl(1.0l, x) sont la meilleure solution, tout simplement parce qu'elles ont de grandes chances d'être exemptes de branchements et qu'elles ne nécessitent pas de retranscrire le résultat d'un entier en une valeur à virgule flottante. Vous devriez probablement indiquer de manière proéminente que vos implémentations en virgule flottante de signum ne retournera pas zéro à cause des particularités des valeurs zéro 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 signe -1/+1 correct, même pour les valeurs zéro.

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