Home > Mobile >  Calculate range of variable types in C
Calculate range of variable types in C

Time:12-01

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

Demo

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

Demo

  • Related