Come faccio a sapere se una variabile intera C è firmata?
-
05-07-2019 - |
Domanda
Come esercizio, vorrei scrivere una macro che mi dice se è firmata una variabile intera. Questo è quello che ho finora e ottengo i risultati che mi aspetto se provo questo su una variabile char con gcc -fsigned-char o -funsigned-char.
#define ISVARSIGNED(V) (V = -1, (V < 0) ? 1 : 0)
È portatile? C'è un modo per farlo senza distruggere il valore della variabile?
Soluzione
#define ISVARSIGNED(V) ((V)<0 || (-V)<0 || (V-1)<0)
non modifica il valore di V. Il terzo test gestisce il caso in cui V == 0.
Sul mio compilatore (gcc / cygwin) funziona per int
e long
ma non per char
o short
.
#define ISVARSIGNED(V) ((V)-1<0 || -(V)-1<0)
fa anche il lavoro in due test.
Altri suggerimenti
Se stai utilizzando GCC puoi utilizzare la parola chiave typeof
per non sovrascrivere il valore:
#define ISVARSIGNED(V) ({ typeof (V) _V = -1; _V < 0 ? 1 : 0 })
Questo crea una variabile temporanea, _V
, che ha lo stesso tipo di V
.
Per quanto riguarda la portabilità, non lo so. Funzionerà su una macchina per complimenti a due (ak.a. tutto ciò che il tuo codice verrà mai eseguito con ogni probabilità), e credo che funzionerà anche su macchine per complimenti e segno-e-grandezza. Come nota a margine, se si utilizza -1
, è possibile che si desideri eseguire il cast da typeof (V)
a <=> per renderlo più sicuro (vale a dire meno probabilità di attivare avvisi).
#define ISVARSIGNED(V) ((-(V) < 0) != ((V) < 0))
Senza distruggere il valore della variabile. Ma non funziona per 0 valori.
Che dire di:
#define ISVARSIGNED(V) (((V)-(V)-1) < 0)
Un approccio diverso a tutte le " rendilo negativo " risponde:
#define ISVARSIGNED(V) (~(V^V)<0)
In questo modo non è necessario avere casi speciali per valori diversi di V, poiché & # 8704; V & # 8712; & # 8484 ;, V ^ V = 0.
Questa semplice soluzione non ha effetti collaterali, incluso il vantaggio di riferirsi a v una sola volta (che è importante in una macro). Usiamo l'estensione gcc & Quot; typeof & Quot; per ottenere il tipo di v, quindi cast -1 per questo tipo:
#define IS_SIGNED_TYPE(v) ((typeof(v))-1 <= 0)
È < = piuttosto che solo < per evitare avvisi del compilatore per alcuni casi (se abilitato).
Perché mai hai bisogno che sia una macro? I modelli sono fantastici per questo:
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;
}
Che funzionerà immediatamente per tutti i tipi integrali fondamentali. Inoltre fallirà in fase di compilazione sui puntatori, cosa che la versione che usa solo sottrazione e confronto probabilmente non lo farà.
EDIT : Oops, la domanda richiede C. Comunque, i modelli sono il modo carino: P
Una caratteristica distintiva della matematica con segno / senza segno è che quando si sposta a destra un numero con segno, viene copiato il bit più significativo. Quando si sposta un numero senza segno, i nuovi bit sono 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
Quindi, in sostanza, questa macro usa un'espressione condizionale per determinare se il bit alto di un numero è impostato. In caso contrario, la macro lo imposta negando bit per bit il numero. Non possiamo fare una negazione aritmetica perché -0 == 0. Quindi spostiamo a destra di 1 bit e testiamo se si è verificata l'estensione del segno.
Questo presuppone l'aritmetica del complemento di 2, ma di solito è un presupposto sicuro.