Calcular el rango de tipos de variables en C.
Eres un traductor de español que traduce documentos de TI. Traduce lo siguiente al español. No traduzcas el código y los resultados en markdown
Aquí un aficionado, así que perdón por la pregunta sin experiencia. K&R ex. 2-1. Utilice las constantes del encabezado y/o el cálculo directo para calcular el rango de varios tipos de variables (char, short, int, long) + tipos en coma flotante. Usé las constantes limits.h para imprimir el rango de los tipos de variables de ancho fijo como referencia. Y todavía tengo que revisar IEEE754.
Mi función máx me da 1 menos para el resultado deseado, como: 0xfe, 0xfffe, etc., por lo que el LSB por alguna razón no cambia a 1. ¿Por qué es eso? Ayer, mientras pensaba en el problema, se me ocurrió la idea de usar el tipo void* para pasar los diversos tipos de variables a las funciones min-max. Para poder desreferenciar el tipo void* almacenando direcciones de números de ancho fijo con longitudes diferentes, tuve la idea de simplemente convertirlo al tipo size_t*, y de alguna manera funciona, y no sé por qué. ¿Es esto legal, nunca lo he visto en mis libros de programación en C? Si no es legal, ¿qué otras posibilidades hay de desreferenciar el tipo void*?
No deseo usar bucles y comprobar desbordamientos, ese método es muy lento.
#include <stdio.h>
#include <float.h>
#include <limits.h>
void unsigned_max(void* data_ptr, size_t data_size) {
int bit = ((int)data_size * 8) - 1;
while(bit) {
*(size_t*)data_ptr |= 1 << bit;
bit--;
}
}
void unsigned_min(void* data_ptr, size_t data_size) {
int bit = ((int)data_size * 8) - 1;
while(bit){
*(size_t*)data_ptr &= 0 << bit;
bit--;
}
}
void signed_min(void* data_ptr, size_t data_size) {
int bit = ((int)data_size * 8) - 1;
*(size_t*)data_ptr |= 1 << bit;
bit--;
while (bit) {
*(size_t*)data_ptr |= 0 << bit;
bit--;
}
}
void signed_max(void* data_ptr, size_t data_size) {
int bit = ((int)data_size * 8) - 1;
*(size_t*)data_ptr &= 0 << bit;
bit--;
while (bit) {
*(size_t*)data_ptr |= 1 << bit;
bit--;
}
}
int main(void) {
unsigned char u_byte = 0;
char byte = 0;
unsigned short ushort_intgr = 0;
short short_intgr = 0;
unsigned int u_intgr = 0;
int intgr = 0;
unsigned long ulong_intgr = 0;
long long_intgr = 0;
char str_output[18] = "Min-max range of";
char delimiter[18] = "================";
/*
* Print ranges of unsigned and signed instances of char, short, int and long
* variables using limits.h constants.
*/
printf("%s unsigned char:\n%x,%x\n", str_output, u_byte, UCHAR_MAX);
printf("%s signed char:\n%x,%x\n", str_output, CHAR_MIN, CHAR_MAX);
printf("%s unsigned short:\n%x,%x\n", str_output, ushort_intgr, USHRT_MAX);
printf("%s signed short:\n%x,%x\n", str_output, SHRT_MIN, SHRT_MAX);
printf("%s unsigned int:\n%x,%x\n", str_output, u_intgr, UINT_MAX);
printf("%s signed int:\n%x,%x\n", str_output, INT_MIN, INT_MAX);
printf("%s unsigned long:\n%x,%x\n", str_output, ulong_intgr, ULONG_MAX);
printf("%s signed long:\n%x,%x\n", str_output, LONG_MIN, LONG_MAX);
printf("%s\n", delimiter);
/*
* Print ranges of unsigned and signed instances of char, short, int and long
* variables using bitwise operations.
*/
printf("%s unsigned char:\n%d,", str_output, u_byte);
unsigned_max(&u_byte, sizeof(u_byte));
printf("%x\n", u_byte);
printf("%s signed char:\n", str_output);
signed_min(&u_byte, sizeof(u_byte));
printf("%x,", u_byte);
signed_max(&u_byte, sizeof(u_byte));
printf("%x\n", u_byte);
printf("%s unsigned short:\n%d,", str_output, ushort_intgr);
unsigned_max(&ushort_intgr, sizeof(ushort_intgr));
printf("%x\n", ushort_intgr);
printf("%s signed short:\n", str_output);
signed_min(&short_intgr, sizeof(short_intgr));
printf("%x,", short_intgr);
signed_max(&short_intgr, sizeof(short_intgr));
printf("%x\n", short_intgr);
printf("%s unsigned int:\n%u,", str_output, u_intgr);
unsigned_max(&u_intgr, sizeof(u_intgr));
printf("%x\n", u_intgr);
printf("%s signed int:\n", str_output);
signed_min(&intgr, sizeof(intgr));
printf("%x,", intgr);
signed_max(&intgr, sizeof(intgr));
printf("%x\n", intgr);
printf("%s unsigned long:\n%lu,", str_output, ulong_intgr);
unsigned_max(&ulong_intgr, sizeof(ulong_intgr));
printf("%x\n", ulong_intgr);
printf("%s signed long:\n", str_output);
signed_min(&long_intgr, sizeof(long_intgr));
printf("%x,", long_intgr);
signed_max(&long_intgr, sizeof(long_intgr));
printf("%x\n", long_intgr);
return 0;
}
Mi resultado es:
Min-max range of unsigned char:
0,ff
Min-max range of signed char:
ffffff80,7f
Min-max range of unsigned short:
0,ffff
Min-max range of signed short:
ffff8000,7fff
Min-max range of unsigned int:
0,ffffffff
Min-max range of signed int:
80000000,7fffffff
Min-max range of unsigned long:
0,ffffffff
Min-max range of signed long:
80000000,7fffffff
Min-max range of unsigned char:
0,fe
Min-max range of signed char:
fe,7e
Min-max range of unsigned short:
0,fffe
Min-max range of signed short:
ffff8000,7ffe
Min-max range of unsigned int:
0,fffffffe
Min-max range of signed int:
80000000,7ffffffe
Min-max range of unsigned long:
0,fffffffe
Min-max range of signed long:
80000000,7ffffffe
También observé la solución del libro K&R, los escritores resuelven habitualmente todo con líneas elegantes. No parece funcionar cuando lo ejecuto, dando ceros para los valores mínimos y máximos. Sé que -0 es todo verdadero en forma de complemento a uno. ¿En ese momento, -0 se podría haber almacenado como 0x1111? Así que 0x1111 >> 1 = 0x0111; 0x0111*(-1) = 0x1000?
printf("signed char min = %d\n", -(char)((unsigned char) -0 >> 1));
</limits.h></float.h></stdio.h>
printf("signed char max = %d\n", (char)((unsigned char) -0 >> 1));
printf("unsigned char max = %u\n", (unsigned char) -0);
...
davy.ai
En relación al problema con el LSB que no se está alternando en la función
unsigned_max()
, parece ser debido a la forma en que está utilizando el operador<<
. Tiene una menor prioridad que los operadores&
y|
, por lo que es mejor usar paréntesis para asegurarse de que se ejecute primero:En cuanto al uso de
size_t*
para desreferenciar punterosvoid*
, generalmente no se recomienda, ya que puede presentar problemas de alineación y de orden de bytes. Una práctica común es utilizar una conversión de tipo a un tipo conocido antes de desreferenciar, como por ejemplo:Alternativamente, también se puede utilizar unión para convertir el puntero
void*
a un tipo específico:Con respecto a la solución K&R, parece estar utilizando la representación de complemento a dos de enteros con signo, donde el bit más significativo se utiliza como bit de signo. La expresión
-0
crea un valor con todos los bits establecidos en 1, que es la representación de complemento a dos del valor negativo más pequeño. Desplazándolo a la derecha en 1 crea el valor positivo más grande, y la conversión de tipo achar
genera los valores correctos para el mínimo y el máximo. Sin embargo, esto puede no funcionar en todas las plataformas, por lo que sigue siendo mejor utilizar las constantesCHAR_MIN
yCHAR_MAX
definidas enlimits.h
.