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?

È stato utile?

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.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top