2009-09-11 16 views
7

Como ejercicio, me gustaría escribir una macro que me dice si se ha firmado una variable entera. Esto es lo que tengo hasta ahora y obtengo los resultados que espero si pruebo esto en una variable char con gcc -fsigned-char o -funsigned-char.¿Cómo puedo saber si una variable entera de C está firmada?

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

¿Es esto portátil? ¿Hay alguna forma de hacerlo sin destruir el valor de la variable?

+0

Este es un problema curioso, pero estoy mucho más intrigado por su uso previsto para esta información es. ¿Alguna posibilidad de compartir? –

+0

Es por eso que C++ tiene RTTI. :) –

+2

@jeffamaphone: En realidad, aquí es donde las plantillas brillan en C++. – sbi

Respuesta

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

no cambia el valor de V. La tercera prueba maneja el caso en que V == 0.

En mi compilador (gcc/cygwin) esto funciona para int y long pero no para char o short.

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

también hace el trabajo en dos pruebas.

+0

Lo mejor que he visto hasta ahora. Portátil, de conformidad con el estándar, preciso hasta donde puedo ver. –

+1

No distingue entre corto firmado/sin signo y char. Esos tipos se promueven a int cuando se evalúa una expresión < == >. – mob

+0

Si desea que funcione para 'short' y' char', probablemente pueda convertir la variable en 'char' antes de usarla. Eso debería manejar la mayoría de los problemas de desbordamiento. Creo que ... –

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

Sin destruir el valor de la variable. Pero no funciona para 0 valores.

¿Qué hay de:

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

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

+0

Sí, no debería tener C & Pd esas cosas redundantes ;-) – ypnos

+0

@plinth, olvidaste los paréntesis "extra" alrededor de 'V' que lo hacen seguro de macroexpansiones locas – rmeador

5

Si está utilizando GCC puede utilizar la palabra clave typeof para no sobrescribir el valor:

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

Esto crea una variable temporal, _V, que tiene la del mismo tipo que V.

En cuanto a la portabilidad, no sé. Funcionará en la máquina complementaria de dos (por ejemplo, todo lo que su código ejecutará con toda probabilidad), y creo que funcionará también en los complementos y las máquinas de signo y magnitud. Como nota al margen, si usa typeof, es posible que desee convertir -1 en typeof (V) para hacerlo más seguro (es decir, menos propenso a disparar advertencias).

+0

En C++, se garantiza que funciona según el estándar, independientemente de la representación entera (para n bits, el valor es 2^n - 1). No tengo el estándar C a mano (ninguno de ellos). –

+0

Yo tampoco, pero recuerdo haber leído en Wikipedia (la fuente de todas las cosas es cierta: P) que el estándar C permite las tres representaciones que enumeré. No es que nadie los use más ... –

-1

Una característica distintiva de las matemáticas con o sin firma es que cuando se desplaza a la derecha un número firmado, se copia el bit más significativo. Cuando se cambia un número sin signo, los nuevos bits son 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 

Así que, básicamente, esta macro utiliza una expresión condicional para determinar si el bit alto de un número se establece. Si no lo es, la macro lo establece por bit negando el número. No podemos hacer una negación aritmética porque -0 == 0. Luego cambiamos a la derecha por 1 bit y probamos si se produjo la extensión del signo.

Esto supone una aritmética de complemento de 2, pero generalmente es una suposición segura.

+0

¿Tiene alguna fuente para estas suposiciones sobre el comportamiento de los cambios de bit? –

+0

El estándar C99 (sección 6.5.7) dice que un cambio a la derecha de un valor negativo firmado es implementación definida. Mi interpretación es que habrá una extensión de signo en una máquina de complemento a 2. Como C no es específico de las arquitecturas de complemento de 2, no saldrán y dirán esto. –

+0

Sería más PC al usar 'CHAR_BITS' en lugar de' 8' mágico. (Sí, lo sé, hay muy pocos de nosotros _no_ trabajando en máquinas donde un byte es de 8 bits. Todavía.) – sbi

1

Esta solución simple no tiene efectos secundarios, incluyendo el beneficio de solo referirse a v una vez (que es importante en una macro). Utilizamos la extensión gcc "typeof" para obtener el tipo de v, y luego echamos -1 a este tipo:

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

Es < = en lugar de sólo < para evitar las advertencias del compilador para algunos casos (cuando está activado).

0

¿Por qué demonios necesitas que sea una macro?Las plantillas son geniales para esto:

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; 
} 

Lo que funcionará de fábrica para todos los tipos integrales fundamentales. También fallará en tiempo de compilación en punteros, que la versión que usa solo resta y comparación probablemente no lo haga.

EDITAR: Vaya, la cuestión requiere C. Sin embargo, las plantillas son la forma más bonita: P

0

Un enfoque diferente para todas las "negativas" que sea respuestas:

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

De esa manera no es necesario tener casos especiales para diferentes valores de V, ya que ∀ V ∈ ℤ, V^V = 0.

Cuestiones relacionadas