2010-12-22 8 views
10

Ocasionalmente me encontraré con un tipo entero (por ejemplo, un entero POSIX con signo tipo off_t) donde sería útil tener una macro para sus valores mínimo y máximo, pero no sé cómo hacer uno que sea verdaderamente portátil.Pregunta C: off_t (y otros tipos de entero con signo) valores mínimos y máximos


Para tipos de enteros sin signo, siempre había pensado que esto era simple. 0 para el mínimo y ~0 para el máximo. Desde entonces he leído varios hilos SO que sugieren usar -1 en lugar de ~0 para la portabilidad. Un hilo interesante con algo de contienda es aquí:
c++ - Is it safe to use -1 to set all bits to true? - Stack Overflow

Sin embargo, incluso después de leer sobre este tema todavía estoy confundido. Además, estoy buscando algo que cumpla con C89 y C99, así que no sé si se aplican los mismos métodos. Supongamos que tengo un tipo de uint_whatever_t. ¿No podría simplemente convertir a 0 primero y luego a modo de complemento de bit? ¿Esto sería aceptable ?:

#define UINT_WHATEVER_T_MAX (~ (uint_whatever_t) 0) 


tipo entero con signo parece que va a ser un hueso duro de roer. He visto varias soluciones posibles diferentes, pero solo one parece ser portátil. O eso o es incorrecto. Lo encontré mientras buscaba en Google un OFF_T_MAX y un OFF_T_MIN. Crédito a Christian Biere:

#define MAX_INT_VAL_STEP(t) \ 
    ((t) 1 << (CHAR_BIT * sizeof(t) - 1 - ((t) -1 < 1))) 

#define MAX_INT_VAL(t) \ 
    ((MAX_INT_VAL_STEP(t) - 1) + MAX_INT_VAL_STEP(t)) 

#define MIN_INT_VAL(t) \ 
    ((t) -MAX_INT_VAL(t) - 1) 

[...] 
#define OFF_T_MAX MAX_INT_VAL(off_t) 


no pude encontrar nada en relación con los diferentes tipos permitidos de representaciones de enteros con signo en C89, C99, pero tiene notas de problemas de portabilidad de números enteros en §J.3.5:

Si los tipos de entero con signo se representan mediante signo y magnitud, el complemento de dos , o el complemento de unos, y si el valor extraordinario es una representación de trap o un valor ordinario (6.2.6.2).

Eso parecería implicar que solo se pueden usar las tres representaciones de números con signo enumerados. ¿Es correcta la implicación y las macros anteriores son compatibles con las tres representaciones?


Otros pensamientos:
Parece que la función macro-como MAX_INT_VAL_STEP() darían un resultado incorrecto si hay bits de relleno. Me pregunto si hay alguna forma de evitar esto.

Lectura a través de signed number representations on Wikipedia se me ocurre que para las representaciones que los tres entero con signo de cualquier tipo entero con signo MAX sería:
poco fuera, todos los bits signo de valor sobre (los tres)
Y su MIN podría ser:
bit de signo en adelante, todos los bits de valor sobre (signo y magnitud)
bit de signo en adelante, todo valor de bits de apagado (los/complemento a dos)

creo que podría probar signo y magnitud al hacer esto:

#define OFF_T_MIN ((((off_t)1 | (~ (off_t) -1)) != (off_t)1) ? /* sign and magnitude minimum value here */ : /* ones and twos complement minimum value here */) 

Entonces, como el signo y la magnitud es el bit de signo activado y todos los bits de valor activados, ¿no sería el mínimo para off_t en ese caso ~ (off_t) 0? Y para el complemento de uno/dos al mínimo, necesitaría alguna forma de desactivar todos los bits de valor, pero dejar el bit de signo activado. No tengo idea de cómo hacer esto sin saber la cantidad de bits de valor. Además, ¿está garantizado que el bit de signo siempre será uno más significativo que el bit de valor más significativo?

Gracias y por favor, hágamelo saber si esto es demasiado tiempo un puesto



EDITAR 12/29/2010 17:00 EST:
Como contestado a continuación por ephemient para obtener el valor máximo de tipo sin signo, (unsigned type)-1 es más correcto que ~0 o incluso ~(unsigned type)0. Por lo que puedo deducir cuando usas -1, es lo mismo que 0-1, que siempre llevará al valor máximo en un tipo sin firmar.

Además, como se puede determinar el valor máximo de un tipo sin signo, es posible determinar cuántos bits de valor están en un tipo sin signo. El crédito a Hallvard B. Furuseth por sus IMAX_BITS()-función como macro que se ha escrito en respuesta a una

IMAX_BITS (INT_MAX) calcula el número de bits en un int, y IMAX_BITS ((unsigned_type question on comp.lang.c

/* Number of bits in inttype_MAX, or in any (1<<b)-1 where 0 <= b < 3E+10 */ 
#define IMAX_BITS(m) ((m) /((m)%0x3fffffffL+1) /0x3fffffffL %0x3fffffffL *30 \ 
        + (m)%0x3fffffffL /((m)%31+1)/31%31*5 + 4-12/((m)%31+3)) 
) -1) calcula el número de bits en unsigned_type. Hasta que alguien implementa enteros de 4 gigabytes, de todos modos :-)

El corazón de mi pregunta sin embargo sigue sin respuesta: ¿cómo determinar los valores mínimo y máximo de un firmó tipo a través de macro. Todavía estoy investigando esto. Quizás la respuesta es que no hay respuesta.

Si no está viendo esta pregunta en StackOverflow, en la mayoría de los casos no podrá ver las respuestas propuestas hasta que sean aceptadas. Se sugiere a view this question on StackOverflow.

+0

De hecho, 'MAX_INT_VAL_STEP' se rompe si hay algunos bits de relleno. Tengo una pregunta relacionada a la que nunca obtuve una respuesta satisfactoria: http://stackoverflow.com/questions/3957252/is-there-any-way-to-compute-the-width-of-an-integer-type-at -compile-time –

+0

@R ¿Qué plataformas apuntadas por un compilador de C tienen bits de relleno en los tipos integrales con signo? Tal vez se puedan manejar con algún '# ifdef' específico para esas plataformas, si es necesario. – Kaz

Respuesta

0

Probablemente quiera mirar limit.h (agregado en C99) este encabezado proporciona macros que se deben establecer para que coincidan con los rangos del compilador.(Ya sea que se proporciona junto con la biblioteca estándar que viene con el compilador, o un reemplazo de la biblioteca estándar de otro fabricante es responsable de hacer las cosas bien)

0

únicas respuestas rápidas:

#define UINT_WHATEVER_T_MAX (~ (uint_whatever_t) 0) se ve bien para mí, la preferencia para -1 es que uint_whatever_t = -1; es más conciso que uint_whatever_t = ~(uint_whatever_t)0;

(CHAR_BIT * sizeof(t)) parece que no se ajusta estrictamente a mí. Tiene razón al rellenar bits, por lo que este valor puede ser considerablemente más que el ancho del tipo, a menos que Posix indique lo contrario sobre off_t.

Por el contrario, los tipos enteros de ancho fijo en C99 no debe tener bits de relleno, por lo que para intN_t estás en un terreno más firme utilizando el tamaño de deducir el ancho. También se les garantiza el complemento a dos.

Eso parecería implicar que sólo los tres mencionados firmados número representaciones se pueden utilizar. Es la implicación correcta

Sí. 6.2.6.2/2 enumera los tres significados permisibles del bit de signo y, por lo tanto, las tres representaciones de número de signo permitido.

es el bit de signo garantizado para siempre ser uno más significativo que el bit de valor significativo más

Se indirectamente requiere para ser más significativo que los bits de valor, por el hecho de (6.2 .6.2/2 nuevamente) que "cada bit que sea un bit de valor tendrá el mismo valor que el mismo bit en la representación del objeto del tipo sin signo correspondiente". Entonces los bits de valor deben ser un rango contiguo comenzando por el menos significativo.

Sin embargo, no puede establecer de forma portátil solo el bit de signo. Lea 6.2.6.2/3 y/4, sobre ceros negativos, y tenga en cuenta que incluso si la implementación utiliza una representación que los tiene en principio, no tiene que respaldarlos, y no hay forma garantizada de generar uno. En una implementación de signo + magnitud, lo que desea es un cero negativo.

[Editar: oh, leí mal, solo necesitas generar ese valor después de que hayas descartado el signo + magnitud, por lo que aún podrías estar bien.

Para ser honesto, me suena un poco molesto si Posix ha definido un tipo entero y no ha proporcionado límites para él. Boo para ellos. Probablemente seguiré con el viejo enfoque de "porting header", donde colocas lo que probablemente funciona en todas partes en un encabezado y documentas que alguien probablemente debería verificarlo antes de compilar el código en cualquier implementación anormal. En comparación con lo que normalmente se tienen que hacer para obtener el código de nadie a trabajar, van a vivir felices con eso.]

5

Sorprendentemente, C promueve tipos hasta int antes de las operaciones aritméticas, con resultados al menos int tamaño. (De forma similar, las rarezas incluyen 'a' caracteres literales que tienen el tipo int, no char.)

int a = (uint8_t)1 + (uint8_t)-1; 
    /* = (uint8_t)1 + (uint8_t)255 = (int)256 */ 
int b = (uint8_t)1 + ~(uint8_t)0; 
    /* = (uint8_t)1 + (int)-1 = (int)0 */ 

Así que #define UINT_WHATEVER_T_MAX (~ (uint_whatever_t) 0) no es necesariamente correcto.

1

Para representaciones signo-magnitud, que es bastante fácil (para los tipos al menos tan ancha como int, de todos modos):

#define SM_TYPE_MAX(type) (~(type)-1 + 1) 
#define SM_TYPE_MIN(type) (-TYPE_MAX(type)) 

Desafortunadamente, firman magnitud representaciones son bastante delgados en el suelo;)

5

Creo que tengo , finalmente resolvió este problema, pero la solución solo está disponible en configure -tiempo, no tiempo de compilación o tiempo de ejecución, por lo que todavía no es idea. Aquí está:

HEADERS="#include <sys/types.h>" 
TYPE="off_t" 
i=8 
while : ; do 
printf "%s\nstruct { %s x : %d; };\n" "$HEADERS" "$TYPE" $i > test.c 
$CC $CFLAGS -o /dev/null -c test.c || break 
i=$(($i+1)) 
done 
rm test.c 
echo $(($i-1)) 

La idea proviene de 6.7.2.1 párrafo 3:

La expresión que especifica la anchura de un campo de bits será una constante de expresión de número entero con un valor no negativo que no excede el ancho de un objeto del tipo que se especificaría si se omitieran los dos puntos y la expresión. Si el valor es cero, la declaración no tendrá declarador.

Estaría encantado si esto lleva a alguna idea para resolver el problema en tiempo de compilación.

+0

R .. gracias por su publicación, esa es una buena idea. Si alguna vez me figura el tiempo de compilación, lo publicaré como respuesta y, si lo resuelves, haz lo mismo. -AQG –

+0

Me alegro de que haya encontrado esta respuesta tardía. Si encuentro una solución mejor, seguramente haré un seguimiento. –

+0

@AnonymousQuestionGuy: Se me ocurrió otra idea que puede ser relevante para su pregunta aquí: http://stackoverflow.com/a/5761398/379897 –

0

máximo Firmado:

#define GENERIC_S_MAX(stype) ((stype) ((1ULL << ((sizeof(stype) * 8) - 1)) - 1ULL)) 

Asumiendo que su sistema utiliza el complemento a dos, el min firmado debe ser:

#define GENERIC_S_MIN(stype) ((stype) -1 - GENERIC_S_MAX(stype)) 

Estos deben ser totalmente portátil, con la excepción de que a largo tiempo es técnicamente una extensión del compilador en C89. Esto también evita el comportamiento indefinido de sobre/desbordamiento de un entero con signo.

+0

Esto supone la falta de bits de relleno. –

+0

Esto es cierto. No estaba familiarizado con los tipos de enteros en C que usan los bits de relleno. –

+0

También asume 'CHAR_BIT == 8', pero eso se soluciona fácilmente sustituyendo' CHAR_BIT' por 8. –

0

Técnicamente no es un macro, pero en la práctica, lo siguiente siempre debe doblarse en un mínimo constante para off_t, o cualquier tipo firmado, independientemente de la representación del letrero. Aunque no estoy seguro de qué es lo que no usa el cumplido de dos, en todo caso.

POSIX requiere un tipo de entero con signo para off_t, por lo que los valores de ancho exacto con signo C99 deberían ser suficientes. Algunas plataformas realmente definen OFF_T_MIN (OSX), pero POSIX desafortunadamente no lo requiere.

#include <stdint.h> 
#include <assert.h> 

#include <sys/types.h> 

    assert(sizeof(off_t) >= sizeof(int8_t) && sizeof(off_t) <= sizeof(intmax_t)); 

    const off_t OFF_T_MIN = sizeof(off_t) == sizeof(int8_t) ? INT8_MIN : 
          sizeof(off_t) == sizeof(int16_t) ? INT16_MIN : 
          sizeof(off_t) == sizeof(int32_t) ? INT32_MIN : 
          sizeof(off_t) == sizeof(int64_t) ? INT64_MIN : 
          sizeof(off_t) == sizeof(intmax_t) ? INTMAX_MIN : 0; 

Lo mismo es útil para obtener el valor máximo.

assert(sizeof(off_t) >= sizeof(int8_t) && sizeof(off_t) <= sizeof(intmax_t)); 

    const off_t OFF_T_MAX = sizeof(off_t) == sizeof(int8_t) ? INT8_MAX : 
          sizeof(off_t) == sizeof(int16_t) ? INT16_MAX : 
          sizeof(off_t) == sizeof(int32_t) ? INT32_MAX : 
          sizeof(off_t) == sizeof(int64_t) ? INT64_MAX : 
          sizeof(off_t) == sizeof(intmax_t) ? INTMAX_MAX : 0; 

Este podría convertirse en una macro usando autoconf o cmake sin embargo.

0

he utilizado el siguiente patrón para resolver el problema (asumiendo que no hay bits de relleno):

((((type) 1 << (number_of_bits_in_type - 1)) - 1) << 1) + 1 

El number_of_bits_in_type se deriva como CHAR_BIT * sizeof (type) como en las otras respuestas.

Básicamente "empujamos" los 1 bits en su lugar, mientras evitamos el bit de signo.

Puede ver cómo funciona esto. Supongamos que el ancho es de 16 bits. Luego tomamos 1 y lo cambiamos a la izquierda por 16 - 2 = 14, produciendo el patrón de bits 0100000000000000. Evitamos cuidadosamente cambiar un 1 en el bit de signo. A continuación, restamos 1 de esto, obteniendo 0011111111111111. ¿Ves a dónde va esto? Cambiamos este valor a la izquierda por 1 obteniendo 0111111111111110, nuevamente evitando el bit de signo. Finalmente agregamos 1, obteniendo 0111111111111111, que es el valor de 16 bits más alto firmado.

Esto debería funcionar bien en el complemento de uno y las máquinas de magnitud de signo, si trabajas en un museo donde tienen tales cosas. No funciona si tienes bits de relleno. Para eso, probablemente todo lo que puede hacer es #ifdef, o cambiar a mecanismos de configuración alternativos fuera del compilador y el preprocesador.

Cuestiones relacionadas