How to programmatically determine the maximum and minimum data limit of int in C?

I am trying to complete Exercise 2.1 from K & R. The exercise reads:

Write a program to determine the ranges of char , short , int and long variables, both signed and unsigned , by printing the corresponding values ​​from standard headers and by direct calculation. It is more difficult if you calculate them: define ranges of various types of floating point.

Printing constants in the standard headers is very simple, like this (for example, only an integer is shown):

 printf("Integral Ranges (from constants)\n"); printf("int max: %d\n", INT_MAX); printf("int min: %d\n", INT_MIN); printf("unsigned int max: %u\n", UINT_MAX); 

However, I want to define the limits programmatically.

I tried this code, which seems to work, but actually it goes into an infinite loop and gets stuck there:

 printf("Integral Ranges (determined programmatically)\n"); int i_max = 0; while ((i_max + 1) > i_max) { ++i_max; } printf("int max: %d\n", i_max); 

Why is this looping? It would seem that when an integer overflows, it goes from 2147483647 to -2147483648. The increased value is obviously less than the previous value, so the cycle should end, but this is not so.

+7
source share
7 answers

Ok, I was about to write a comment, but it's too long ...

Can sizeof use sizeof ?

If true, then there is an easy way to find the maximum value for any type:

For example, I will find the maximum value for an integer:

Definition: INT_MAX = (1 << 31) - 1 for a 32-bit integer (2 ^ 31 - 1)

The previous definition overflows if we use integers to calculate int max, so it needs to be adapted correctly:

 INT_MAX = (1 << 31) - 1 = ((1 << 30) * 2) - 1 = ((1 << 30) - 1) * 2 + 2) - 1 = ((1 << 30) - 1) * 2) + 1 

And using sizeof :

 INT_MAX = ((1 << (sizeof(int)*8 - 2) - 1) * 2) + 1 

You can do the same for any type of signed / unsigned by simply reading the rules for each type.

+7
source

So there really was n’t a jam in an infinite loop. C code is usually so fast that I believe that it does not work if it does not exit immediately.

In the end, he returned the correct answer after I let him work for 10 seconds. It turns out that 2,147,483,647 increments take up quite a few cycles.

It should also be noted that I compiled with cc -O0 to disable optimizations, so this was not a problem.

A faster solution might look something like this:

 int i_max = 0; int step_size = 256; while ((i_max + step_size) > i_max) { i_max += step_size; } while ((i_max + 1) > i_max) { ++i_max; } printf("int max: %d\n", i_max); 

However, since overflowing with a sign is vague behavior, it is probably a terrible idea to ever try to guess this programmatically in practice. Better to use INT_MAX .

+1
source

Suppose for a two-component processor, use unsigned math:

 unsigned ... smax, smin; smax = ((unsigned ...)0 - (unsigned ...)1) / (unsigned ...) 2; smin = ~smax; 
0
source

As pointed out here in other solutions, trying to overflow an integer in C is undefined behavior, but at least in this case, I think you can get the right answer even from the UB thing:

The fact is that if you increase the value and compare the new value with the last one, you always get a larger value, with the exception of overflow (in this case, you get a smaller or equal value --- you don’t have “I have more values ​​than the case when overflow). So you can try at least:

 int i_old = 0, i = 0; while (++i > i_old) i_old = i; printf("MAX_INT guess: %d\n", i_old); 

After this loop, you will get the expected overflow, and old_i keep the last valid number. Of course, if you go down, you have to use this piece of code:

 int i_old = 0, i = 0; while (--i < i_old) i_old = i; printf("MIN_INT guess: %d\n", i_old); 

Of course, UB can even mean a program shutdown run (in this case, you will need to put a trace in order to get at least the last value)

By the way, in ancient times K & R, integers had a width of 16 bits, the value was easily accessible by counting (easier than now, try translating 64-bit integers from 0 to)

0
source

I would use the properties of two additions to calculate the values.

 unsigned int uint_max = ~0U; signed int int_max = uint_max >> 1; signed int int_min1 = (-int_max - 1); signed int int_min2 = ~int_max; 

2 ^ 3 is 1000 . 2 ^ 3 - 1 is 0111 . 2 ^ 4 - 1 is 1111 .

w is the bit length of your data type.

uint_max is 2 ^ w - 1 or 111...111 . This effect is achieved with ~0U .

int_max is 2 ^ (w-1) - 1 or 0111...111 . This effect can be achieved by shifting the uint_max bits 1 bit to the right. Since uint_max is an unsigned value, a logical shift is applied by the >> operator, that is, it adds leading zeros instead of expanding the sign bit.

int_min is -2 ^ (w-1) or 100...000 . In two additions, the most significant bit has a negative weight!

Here's how to visualize the first expression to evaluate int_min1 :

 ... 011...111 int_max +2^(w-1) - 1 100...000 (-int_max - 1) -2^(w-1) == -2^(w-1) + 1 - 1 100...001 -int_max -2^(w-1) + 1 == -(+2^(w-1) - 1) ... 

Addition 1 will move down, and subtraction 1 will move up. First, we int_max to generate the correct int value, then subtract 1 to get int_min . We cannot simply negate (int_max + 1) because it will exceed the int_max value int_max , the largest value of int .

Depending on which version of C or C ++ you are using, the expression -(int_max + 1) will either become a 64-bit signed integer, preserving the signature, but sacrificing the original width in bits, or it will become a 32-bit unsigned integer, keeping the original bit width, but sacrificing the signature. We need to declare int_min programmatically to preserve the actual value of int .

If this bit (or byte) is too complicated for you, you can just do ~int_max , noting that int_max is 011...111 and int_min is 100...000 .

Keep in mind that these methods that I mentioned here can be used for any bit width w of an integer data type. They can be used for char , short , int , long , as well as long long . Keep in mind that integer literals are almost always 32-bit by default, so you may have to cast 0U to a data type with an appropriate bit width before BITING, NOT ANNOUNCING. But beyond this, these methods are based on fundamental mathematical principles for representing an integer from two additions. However, they will not work if your computer uses a different way of representing integers, such as padding or the most significant character bit.

0
source

The assignment says that "printing the appropriate values ​​from standard headers" is allowed, and in the real world this is exactly what you will do. As your professor wrote, direct computing is more difficult, and why complicate the situation yourself when you are working on another interesting problem and want to get a result? Find the constants in <limits.h> , for example, INT_MIN and INT_MAX .

Since this is homework and you want to solve it yourself, here are some tips.

The language standard technically allows any of three different representations for signed numbers: two with an addition, one with an addition, and a sign and magnitude. Of course, every computer built in the last fifty years has used two add-ons (with the partial exception of legacy code for certain Unisys mainframes), but if you really want to use a legal language, you can calculate the smallest number for each of the three possible representations and find the minimum comparing them.

Trying to find the answer by overflowing or dropping the value with a sign does not work! This is vague behavior ! Theoretically, but not in practice, you can increase the unsigned value of the same width, convert to the corresponding type with a sign, and compare with the result of casting the previous or next unsigned value. For a 32-bit long this may just be valid; it does not scale to a machine with a width of 64 bits long .

You want to use bitwise operators, in particular ~ and << , to calculate the largest and smallest value for each type. Note: CHAR_BITS * sizeof(x) gives you the number of bits in x , and a left shift of 0x01UL is one less, and then casting to the desired type sets the most significant bit.

For floating point values, the only portable way is to use constants in <math.h> ; Floating-point values ​​may or may not represent positive and negative infinity, are not limited to the use of any particular format. However, if your compiler supports the optional G application to the C11 standard, which specifies IEC 60559 complex arithmetic, then dividing a non-zero floating point number by zero will be defined as creating infinity, which allows you to “calculate” infinity and negative infinity. If so, the implementation will be #define __STDC_IEC_559_COMPLEX__ equal to 1.

If you find that infinity is not supported in your implementation, for example by checking if INFINITY and -INFINITY infinite, you will want to use -HUGE_VAL and HUGE_VAL -HUGE_VAL .

0
source
 #include <stdio.h> int main() { int n = 1; while(n>0) { n=n<<1; } int int_min = n; int int_max = -(n+1); printf("int_min is: %d\n",int_min); printf("int_max is: %d\n", int_max); return 0; } 
0
source

All Articles