a hobbyist here so sorry for the nooby question. K&R ex. 2-1. Use header constants and/or direct computation to calculate range of various variable types (char, short, int, long) floating types. I used limits.h constants to print the range of fixed width variable types for reference. And I still have to check IEEE754.
My max functions gives me 1 less for the desired result, like: 0xfe, 0xfffe, etc so the LSB for some reason is not toggled to 1. Why is that? Yesterday, while thinking on the problem, I came up with the idea to use void* type to pass the various variable types to min-max functions. To be able to derefence void* type storing addresses of fixed width numbers with different length, I had the idea to simply cast it to size_t* type, and somehow it works, and I don't know why. Is this even legal, I've never seen it in my C programming books. If it is not legal, what other possibilites does one have to derefence void* type?
I do not wish to use loops and check overflows, that method is very slow.
#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;
}
My output is:
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
I also looked at the K&R solution book, the writers solve usually everything with elegant one-liners. It doesn't seem to work when I run it, giving back zeroes for min and max values. I know that -0 is every bit is true in one's complement form. That time -0 could have been stored as 0x1111? So 0x1111 >> 1 = 0x0111; 0x0111*(-1) = 0x1000?
printf("signed char min = %d\n", -(char)((unsigned char) -0 >> 1));
printf("signed char max = %d\n", (char)((unsigned char) -0 >> 1));
printf("unsigned char max = %u\n", (unsigned char) -0);
...
CodePudding user response:
On a two's complement machine, you need
printf("signed char min: %hhd\n", -(signed char)((unsigned char)~0 >> 1) - 1);
printf("signed char max: %hhd\n", (signed char)((unsigned char)~0 >> 1));
printf("unsigned char max: %hhu\n", (unsigned char)~0);
printf("signed short min: %hd\n", -(signed short)((unsigned short)~0 >> 1) - 1);
printf("signed short max: %hd\n", (signed short)((unsigned short)~0 >> 1));
printf("unsigned short max: %hu\n", (unsigned short)~0);
printf("signed int min: %d\n", -(signed int)(~0U >> 1) - 1);
printf("signed int max: %d\n", (signed int)(~0U >> 1));
printf("unsigned int max: %u\n", ~0U);
printf("signed long min: %ld\n", -(signed long)(~0UL >> 1) - 1);
printf("signed long max: %ld\n", (signed long)(~0UL >> 1));
printf("unsigned long max: %lu\n", ~0UL);
printf("signed long long min: %lld\n", -(signed long long)(~0ULL >> 1) - 1);
printf("signed long long max: %lld\n", (signed long long)(~0ULL >> 1));
printf("unsigned long long max: %llu\n", ~0ULL);
Note: char
is not always the same as signed char
, so we need to use signed char
. We could, however, simply use short
and int
instead of signed short
and signed int
.
That said, the correct and platform-independent approach is to use the compiler-provided defines.
#include <limits.h>
printf("signed char min: %hhd\n", SCHAR_MIN);
printf("signed char max: %hhd\n", SCHAR_MAX);
printf("unsigned char max: %hhu\n", UCHAR_MAX);
printf("signed char min: %hd\n", SHRT_MIN);
printf("signed char max: %hd\n", SHRT_MAX);
printf("unsigned char max: %hu\n", USHRT_MAX);
printf("signed int min: %d\n", INT_MIN);
printf("signed int max: %d\n", INT_MAX);
printf("unsigned int max: %u\n", UINT_MAX);
printf("signed long min: %ld\n", LONG_MIN);
printf("signed long max: %ld\n", LONG_MAX);
printf("unsigned long max: %lu\n", ULONG_MAX);
printf("signed long long min: %lld\n", LLONG_MIN);
printf("signed long long max: %lld\n", LLONG_MAX);
printf("unsigned long long max: %llu\n", ULLONG_MAX);