¿Cómo puedo saber si una variable entera de C tiene signo?
-
05-07-2019 - |
Pregunta
Como ejercicio, me gustaría escribir una macro que me diga si una variable entera tiene signo.Esto es lo que tengo hasta ahora y obtengo los resultados que espero si intento esto en una variable char con gcc -fsigned-char o -funsigned-char.
#define ISVARSIGNED(V) (V = -1, (V < 0) ? 1 : 0)
¿Es esto portátil?¿Hay alguna manera de hacer esto sin destruir el valor de la variable?
Solución
#define ISVARSIGNED(V) ((V)<0 || (-V)<0 || (V-1)<0)
no cambia el valor de V.La tercera prueba maneja el caso donde 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.
Otros consejos
Si usa GCC, puede usar 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 el mismo tipo que V
.
En cuanto a la portabilidad, no lo sé. Funcionará en la máquina de cumplidos de dos (también conocido como todo lo que su código ejecutará con toda probabilidad), y creo que funcionará también en las máquinas de cumplidos y signos y magnitudes. Como nota al margen, si usa -1
, es posible que desee convertir typeof (V)
a <=> para hacerlo más seguro (es decir, menos probable que active advertencias).
#define ISVARSIGNED(V) ((-(V) < 0) != ((V) < 0))
Sin destruir el valor de la variable. Pero no funciona para valores 0.
¿Qué pasa con:
#define ISVARSIGNED(V) (((V)-(V)-1) < 0)
Un enfoque diferente para todos los " que sea negativo " respuestas:
#define ISVARSIGNED(V) (~(V^V)<0)
De esa manera no es necesario tener casos especiales para diferentes valores de V, ya que & # 8704; V & # 8712; & # 8484 ;, V ^ V = 0.
Esta solución simple no tiene efectos secundarios, incluido el beneficio de referirse solo a v una vez (lo cual es importante en una macro). Usamos la extensión gcc & Quot; typeof & Quot; para obtener el tipo de v, y luego convertir -1 a este tipo:
#define IS_SIGNED_TYPE(v) ((typeof(v))-1 <= 0)
Es < = en lugar de solo < para evitar advertencias del compilador en algunos casos (cuando está habilitado).
¿Por qué demonios necesitas que sea una macro? Las plantillas son excelentes 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;
}
Que funcionará de forma inmediata 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 hará.
EDITAR : Vaya, la pregunta requiere C. Aún así, las plantillas son la mejor manera: P
Una característica distintiva de las matemáticas con signo / sin signo es que cuando se desplaza a la derecha un número con signo, se copia el bit más significativo. Cuando 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
Entonces, básicamente, esta macro usa una expresión condicional para determinar si se establece el bit alto de un número. Si no es así, la macro lo establece negando bit a bit el número. No podemos hacer una negación aritmética porque -0 == 0. Luego nos desplazamos a la derecha en 1 bit y probamos si se produjo la extensión del signo.
Esto supone la aritmética del complemento a 2, pero generalmente es una suposición segura.