2009-09-11 8 views
7

Comme un exercice, je voudrais écrire une macro qui me dit si une variable entière est signée. C'est ce que j'ai jusqu'ici et j'obtiens les résultats que j'attends si j'essaie ceci sur une variable char avec gcc -fsigned-char ou -funsigned-char.Comment savoir si une variable C integer est signée?

#define ISVARSIGNED(V) (V = -1, (V < 0) ? 1 : 0) 

Est-ce portable? Y a-t-il un moyen de le faire sans détruire la valeur de la variable?

+0

Ceci est un problème curieux, mais je suis plus intrigué par l'utilisation que vous en ferez pour cette information. est. Une chance de partager? –

+0

C'est pourquoi C++ a RTTI. :) –

+2

@jeffamaphone: En fait, c'est là que les templates brillent en C++. – sbi

Répondre

4
#define ISVARSIGNED(V) ((V)<0 || (-V)<0 || (V-1)<0) 

ne change pas la valeur de V. Le troisième test traite le cas où V == 0.

Sur mon compilateur (gcc/Cygwin) cela fonctionne pour int et long mais pas pour char ou short.

#define ISVARSIGNED(V) ((V)-1<0 || -(V)-1<0) 

fait également le travail dans deux tests.

+0

Le meilleur que j'ai vu pour le moment. Portable, conforme aux normes, précis autant que je peux voir. –

+1

Ne fait pas la distinction entre short/signed et unsigned. Ces types sont promus à int lors de l'évaluation d'une expression < == >. – mob

+0

Si vous voulez que ça marche pour 'short' et' char', vous pouvez probablement transformer la variable en 'char' avant de l'utiliser. Cela devrait gérer la plupart des problèmes de débordement. Je pense ... –

5
#define ISVARSIGNED(V) ((-(V) < 0) != ((V) < 0)) 

Sans détruire la valeur de la variable. Mais ne fonctionne pas pour 0 valeurs.

Qu'en est-:

#define ISVARSIGNED(V) (((V)-(V)-1) < 0) 
+3

#define ISVARSIGNED (V) ((-V <0)! = (V <0)) – plinth

+0

ouais je ne devrais pas avoir C & Pd ce truc redondant ;-) – ypnos

+0

@plinth, vous avez oublié les parens "supplémentaires" autour de "V" qui le rendent à l'abri des expansions de macro folles – rmeador

5

Si vous utilisez GCC, vous pouvez utiliser le mot-clé typeof pour ne pas écraser la valeur:

#define ISVARSIGNED(V) ({ typeof (V) _V = -1; _V < 0 ? 1 : 0 }) 

Cela crée une variable temporaire, _V, qui a la même type que V.

En ce qui concerne la portabilité, je ne sais pas. Il travaillera sur une machine à complimenter à deux (c.-à-d. Tout ce que votre code fonctionnera probablement selon toute probabilité), et je crois que cela fonctionnera aussi sur ses machines de compliment et de signature. En remarque, si vous utilisez typeof, vous pouvez lancer -1 à typeof (V) pour le rendre plus sûr (c'est-à-dire moins susceptible de déclencher des avertissements).

+0

En C++, il est garanti de travailler avec la norme, quelle que soit la représentation de l'entier (pour n bits, la valeur est 2^n - 1). Je n'ai pas le C standard à portée de main (aucun d'entre eux). –

+0

Moi non plus, mais je me souviens avoir lu sur Wikipédia (la source de tout vrai: P) que la norme C permet les trois représentations que j'ai énumérées. Pas que personne ne les utilise plus ... –

-1

Une caractéristique distinctive des mathématiques signées/non signées est que lorsque vous faites un décalage vers la droite d'un nombre signé, le bit le plus significatif est copié. Lorsque vous passez un nombre non signé, les nouveaux bits sont 0.

#define HIGH_BIT(n) ((n) & (1 << sizeof(n) * CHAR_BITS - 1)) 
#define IS_SIGNED(n) (HIGH_BIT(n) ? HIGH_BIT(n >> 1) != 0 : HIGH_BIT(~n >> 1) != 0 

Donc, fondamentalement, cette macro utilise une expression conditionnelle pour déterminer si le bit d'un nombre est fixé. Si ce n'est pas le cas, la macro le définit par bit en niant le nombre. Nous ne pouvons pas faire de négation arithmétique parce que -0 == 0. Nous décalons ensuite à droite de 1 bit et testons si l'extension de signe s'est produite.

Cela suppose l'arithmétique du complément à 2, mais c'est généralement une hypothèse sûre.

+0

Avez-vous une source pour ces hypothèses sur le comportement des changements de bits? –

+0

La norme C99 (section 6.5.7) indique qu'un décalage vers la droite d'une valeur négative signée est défini par l'implémentation. Mon interprétation est qu'il y aura une extension de signe sur une machine à complément de 2. Puisque C n'est pas spécifique aux architectures complémentaires à 2, ils ne vont pas le dire. –

+0

Vous seriez plus PC en utilisant 'CHAR_BITS' au lieu de la magie' 8'. (Oui, je sais, il n'y a qu'un très petit nombre d'entre nous qui ne travaillent pas sur des machines où un octet est 8bit.) – sbi

1

Cette solution simple n'a pas d'effets secondaires, y compris l'avantage de se référer uniquement à v une fois (ce qui est important dans une macro). Nous utilisons l'extension gcc « typeof » pour obtenir le type de v, puis coulé -1 à ce type:

#define IS_SIGNED_TYPE(v) ((typeof(v))-1 <= 0) 

Il est < = plutôt que < pour éviter les avertissements du compilateur pour certains cas (lorsqu'il est activé).

0

Pourquoi diable avez-vous besoin d'une macro?Les modèles sont parfaits pour cela:

template <typename T> 
bool is_signed(T) { 
    static_assert(std::numeric_limits<T>::is_specialized, "Specialize std::numeric_limits<T>"); 
    return std::numeric_limits<T>::is_signed; 
} 

Ce qui fonctionnera immédiatement pour tous les types d'intégrales fondamentales. Il échouera également à la compilation sur les pointeurs, ce que la version utilisant seulement la soustraction et la comparaison ne le fera probablement pas.

EDIT: Oops, la question nécessite C. Cependant, les modèles sont la bonne façon: P

0

Une approche différente de toutes les réponses "négatives" rendre:

#define ISVARSIGNED(V) (~(V^V)<0) 

De cette façon Il n'est pas nécessaire d'avoir des cas spéciaux pour différentes valeurs de V, puisque ∀ V ∈ ℤ, V^V = 0.