James Kuyper said:
The most important differences between unsigned and signed
types are not in their performance, but their behavior. ...
The easiest way to determine the limits of the C types is to
use the macros from <limits.h>, but I presume that would defeat
the point of the exercise.
Indeed, and under C90, not all integer types have known limit
macros, e.g. ptrdiff_t.
There [are] safe and quite trivial methods of computing the
range of an unsigned type, but most of the ways I can think
of for computing the range of a signed type are subject to
serious portability issues.
It's impossible in C99, but the maximum is certainly achievable
in C90.
ptrdiff_t max, new;
for (max = 32767; (new = 2ul * max + 1) > max; )
max = new;
printf("%ld\n", (long) max);
Whether the minimum is possible depends on whether you believe
XXX_MIN is limited to -XXX_MAX or -XXX_MAX-1.
I thought you all might like to see what I ended up with for this. I have to
admit that this code does take advantage of formally undefined behaviour;
notably that signed types will loop around to their maximum when the minimum is
exceeded, and vice versa. However, this does seem to be quite common behaviour,
and - more importantly - I cannot find another way to do it.
I also have not included functions to compute the range of floating point types
(although I do print these values from the <floats.>h macros) since I have not
yet found a satisfactory method for this.
Also, I realise that I could probably make this a bit tidier and less
function-happy, but I wanted to restrict myself to language features that had
been covered by K&R up to the point where this exercise appears.
So, here it is (warning! large code dump ahead):
<CODE>
/*
* K&R Exercise 2-1
*
* Determine the ranges of char, short, int,
* and long variables, both signed and
* unsigned, by printing appropriate values
* from standard headers and by direct
* computation. Harder if you compute them:
* determine the ranges of the various
* floating point types.
*
* Author: Thomas Scrace <
[email protected]>
*/
#include <stdio.h>
#include <limits.h>
#include <float.h>
signed char scmin();
signed short ssmin();
signed int simin();
signed long slmin();
main()
{
unsigned char cmn, cmx;
signed char scmn, scmx;
unsigned short smx;
short ssmn;
unsigned int imx;
int simn;
unsigned long lmx;
long slmn;
cmx = -1;
cmn = cmx + 1;
scmn = scmin();
scmx = scmn - 1;
smx = -1;
ssmn = ssmin();
imx = -1;
simn = simin();
lmx = -1;
slmn = slmin();
printf("\n*****************INTEGRAL TYPES********************\n\n");
printf("-----------values from standard headers-----------\n\n");
printf("%21d : char : %d\n", 0, UCHAR_MAX);
printf("%21d : signed char: %d\n", CHAR_MIN, CHAR_MAX);
printf("%21d : short : %d\n", 0, USHRT_MAX);
printf("%21d :signed short: %d\n", SHRT_MIN, SHRT_MAX);
printf("%21d : int : %u\n", 0, UINT_MAX);
printf("%21d : signed int : %d\n", INT_MIN, INT_MAX);
printf("%21d : long : %lu\n", 0, ULONG_MAX);
printf("%21ld :signed long : %ld\n", LONG_MIN, LONG_MAX);
printf("\n-----------values by direct computation-----------\n\n");
printf("%21d : char : %d\n", cmn, cmx);
printf("%21d : signed char: %d\n", scmn, scmx);
printf("%21hd : short : %d\n", smx + 1, smx);
printf("%21d :signed short: %hd\n", ssmn, ssmn - 1);
printf("%21d : int : %u\n", imx + 1, imx);
printf("%21d : signed int : %d\n", simn, simn - 1);
printf("%21d : long : %lu\n", lmx + 1, lmx);
printf("%21ld :signed long : %ld\n", slmn, slmn - 1);
printf("\n**************FLOATING-POINT TYPES*****************\n\n");
printf("-----------values from standard headers-------------\n\n");
printf("%21.6e : float : %0.6e\n", FLT_MIN, FLT_MAX);
printf("%21.10e : double : %0.10e\n", DBL_MIN, DBL_MAX);
return 0;
}
/* scmin: return the minimum value of a signed char */
signed char scmin()
{
signed char c, tmp;
for (c = tmp = -2; c < 0; c = c * 2)
tmp = c;
return tmp;
}
/* ssmin: return the minimum value of a signed short */
signed short ssmin()
{
signed short s, tmp;
for (s = tmp = -2; s < 0; s = s * 2)
tmp = s;
return tmp;
}
/* simin: return the minimum value of a signed int */
signed int simin()
{
signed int i, tmp;
for (i = tmp = -2;i < 0; i = i * 2)
tmp = i;
return tmp;
}
/* slmin: return the minimum value of a signed long */
signed long slmin()
{
signed long l, tmp;
for (l = tmp = -2; l < 0; l = l * 2)
tmp = l;
return tmp;
}
</CODE>
The output, on my system, is as follows:
<OUTPUT>
*****************INTEGRAL TYPES********************
-----------values from standard headers-----------
0 : char : 255
-128 : signed char: 127
0 : short : 65535
-32768 :signed short: 32767
0 : int : 4294967295
-2147483648 : signed int : 2147483647
0 : long : 18446744073709551615
-9223372036854775808 :signed long : 9223372036854775807
-----------values by direct computation-----------
0 : char : 255
-128 : signed char: 127
0 : short : 65535
-32768 :signed short: 32767
0 : int : 4294967295
-2147483648 : signed int : 2147483647
0 : long : 18446744073709551615
-9223372036854775808 :signed long : 9223372036854775807
**************FLOATING-POINT TYPES*****************
-----------values from standard headers-------------
1.175494e-38 : float : 3.402823e+38
2.2250738585e-308 : double : 1.7976931349e+308
</OUTPUT>