[comp.sources.misc] A C configuration enquirer

steven@cwi.nl.UUCP (Steven Pemberton) (08/05/87)

config.c - a C configuration enquirer
Author: Steven Pemberton, CWI, Amsterdam

This program determines properties of your machine and C compiler, such as
the number of bits used for certain data-types, the accuracy of float and
double, and so on.

The original purpose of the program was to generate a header-file for a
large piece of software that must be as portable as possible. However, it is
also a good check for a new compiler. It is the descendent of a similar
program I posted a year or so ago. However it is completely rewritten, and
incorporates much experience with the use of that program, and ideas from
users of it.

Output is produced as C style comments so that the program can be used to
produce a .h file with minimum upheaval.

Any ideas for future enhancements, and all fixes to make it run on machines
where it doesn't currently run, will be gratefully received. Please mail me
at
	steven@cwi.nl (new style) or seismo!mcvax!steven (old style)

Steven Pemberton, Centre for Mathematics and Computer Science
Amsterdam, The Netherlands

: This is a shell archive.
: Extract with 'sh this_file'.
echo 'Start of pack.out, part 01 out of 01:'
echo 'x - Makefile'
sed 's/^X//' > 'Makefile' << 'EOF'
Xconfig:
X	cc config.c -o config
EOF
echo 'x - READ.ME'
sed 's/^X//' > 'READ.ME' << 'EOF'
X		config.c - a C configuration enquirer
X		Author: Steven Pemberton, CWI, Amsterdam
X
XThis program determines properties of your machine and C compiler, such as
Xthe number of bits used for certain data-types, the accuracy of float and
Xdouble, and so on.
X
XThe original purpose of the program was to generate a header-file for a
Xlarge piece of software that must be as portable as possible. However, it is
Xalso a good check for a new compiler. It is the descendent of a similar
Xprogram I posted a year or so ago. However it is completely rewritten, and
Xincorporates much experience with the use of that program, and ideas from
Xusers of it.
X
XThe program only works if overflows are ignored by the C system or are
Xcatchable by signal().
X
XIf your C system is not unix but does have signal/setjmp, compile with
X	cc -DSIGNAL config.c
Xotherwise with
X	cc config.c
XDon't use any optimisation flags.
XSome compilers need a -f flag for floating point.
X
XYou may need to add some calls to signal() for other sorts of exception on
Xyour machine than SIGFPE, and SIGOVER. See lines beginning #ifdef SIGNAL
Xlater in the program.
X
XOutput is produced as C style comments so that the program can be used to
Xproduce a .h file with minimum upheaval.
X
XI apologise unreservedly for the contorted use of the preprocessor...
X
XIf your C preprocessor doesn't have the predefined __FILE__ macro, and you
Xwant to call the file anything other than config.c, change the first
X#define command accordingly.
X
XAny ideas for future enhancements, and all fixes to make it run on machines
Xwhere it doesn't currently run, will be gratefully received. Please mail me
Xat
X	steven@cwi.nl (new style) or seismo!mcvax!steven (old style)
X
XSteven Pemberton, Centre for Mathematics and Computer Science
XAmsterdam, The Netherlands
EOF
echo 'x - config.1'
sed 's/^X//' > 'config.1' << 'EOF'
X.TH config 8 local
X.SH NAME
Xconfig \(em print details of your machine and C compiler configuration
X.SH SYNOPSIS
Xconfig
X.SH DESCRIPTION
XConfig determines and prints out several properties of the C compiler it is
Xcompiled with and the machine it is run on.
XAmong the properties it gives are
X.br
X.in +1c
X\(em the number of bits in a char, and whether chars are signed or not;
X.br
X\(em the maximum short, int, and long;
X.br
X\(em alignment values for char, short, int and long;
X.br
X\(em number of bits for char and int pointers, with a warning if they are longer
Xthan int;
X.in -1c
X.sp
Xand then for float and double:
X.br
X.in +1c
X\(em the base used;
X.br
X\(em number of significant digits;
X.br
X\(em certain minumum and maximum values;
X.br
X\(em whether arithmetic rounds or chops;
X.br
X\(em whether a hidden bit is used or not;
X.br
X\(em etc.
X.in -1c
X.sp
XFinally the total amount of memory that can be allocated by malloc(3) is
Xprinted.
X.SH DIAGNOSTICS
XThe output is printed as a series of C comments, so that the program can be
Xused to produce header files for C programs without too much upheaval.
X.SH BUGS
XThe program won't work if overflow causes a trap,
Xand the trap can't be caught by signal(2).
EOF
echo 'x - config.c'
sed 's/^X//' > 'config.c' << 'EOF'
X/* Determine some properties of C types on your machine/compiler
X   Author: Steven Pemberton, CWI, Amsterdam; steven@cwi.nl
X   Bugfixes and upgrades gratefully received.
X
X   The program only works if overflows are ignored by the C system or are
X   catchable by signal().
X
X   If your C system is not unix but does have signal/setjmp, compile with
X	cc -DSIGNAL config.c
X   otherwise with
X	cc config.c
X   Don't use any optimisation flags: the program won't work if you do.
X   Some compilers need a -f flag for floating point.
X
X   You may need to add some calls to signal() for other sorts of exception
X   on your machine than SIGFPE, and SIGOVER. See lines beginning #ifdef
X   SIGNAL later in the program.
X
X   Output is produced as C style comments so that the program can be used to
X   produce a .h file with minimum upheaval.
X
X   I apologise unreservedly for the contorted use of the preprocessor...
X
X   If your C preprocessor doesn't have the predefined __FILE__ macro, and
X   you want to call this file anything other than config.c, change the
X   following #define command accordingly.
X*/
X
X#ifndef __FILE__
X#define __FILE__ "config.c"
X#endif
X
X#ifndef PASS
X#define PASS 1
X
X#include <stdio.h>
X
X#ifndef SIGNAL
X#ifdef unix
X#define SIGNAL
X#endif /*unix*/
X#endif /*SIGNAL*/
X
X#ifdef SIGNAL
X
X#include <signal.h>
X#include <setjmp.h>
X
X	jmp_buf lab;
X	overflow(sig) int sig; { /* what to do on overflow/underflow */
X		(void) signal(sig, overflow);
X		longjmp(lab, 1);
X	}
X
X#else /*!SIGNAL*/
X	/* Dummy routines instead */
X	int lab=1;
X	int setjmp(lab) int lab; { return(0); }
X
X#endif /*SIGNAL*/
X
Xmain() {
X	int bits; /* the number of bits per unit returned by sizeof() */
X	int dprec, eprec, basic(), fprop(), dprop(), eprop();
X	char *malloc();
X	unsigned int size;
X	long total;
X
X#ifdef SIGNAL
X#ifdef SIGFPE
X	(void) signal(SIGFPE, overflow);
X#endif
X#ifdef SIGOVER
X	(void) signal(SIGOVER, overflow);
X#endif
X/* Add more calls as necessary */
X#endif /* SIGNAL */
X
X	if (setjmp(lab)!=0) { printf("Unexpected over/underflow\n"); exit(1); }
X
X	bits=  basic();
X	(void) fprop(bits);
X	dprec= dprop(bits);
X	eprec= eprop();
X	printf("\n");
X	if (eprec!=dprec)
X		printf("/\* Expressions are evaluated in a %s %s %d %s *\/\n",
X			eprec>dprec ? "higher" : "lower (tut!)",
X			"precision than double, using", eprec, "base digits");
X	else printf("/\* Expressions are evaluated in double precision *\/\n");
X
X	/* An extra goody: the approximate amount of data-space */
X	/* Allocate store until no more available */
X	size=1<<((bits*sizeof(int))-2);
X	total=0;
X	while (size!=0) {
X		while (malloc(size)!=(char *)NULL) total+=(size/2);
X		size/=2;
X	}
X
X	printf("\n/\* Memory mallocatable ~= %ld Kbytes *\/\n",
X						(total+511)/512);
X}
X
Xint basic() {
X	/* The properties of the basic types.
X	   Returns number of bits per sizeof unit */
X
X	char c; int bits;
X
X	if (setjmp(lab)!=0) { printf("\nUnexpected under/overflow\n"); exit(1); }
X
X	/* Calculate number of bits per character *************************/
X	c=1; bits=0;
X	do { c=c<<1; bits++; } while(c!=0);
X	c= (char)(-1);
X	printf("/\* Char = %d bits, %ssigned *\/\n", sizeof(c)*bits,
X			((int)c)<0?"":"un");
X
X	/* Shorts, ints and longs *****************************************/
X	sprop();
X	iprop();
X	lprop();
X
X	if (setjmp(lab)!=0) { printf("\nUnexpected under/overflow\n"); exit(1); }
X
X	/* Alignment constants ********************************************/
X	printf("/\* Alignments for char=%d short=%d int=%d long=%d *\/\n",
X		sizeof(struct{char i; char c;})-sizeof(char),
X		sizeof(struct{short i; char c;})-sizeof(short),
X		sizeof(struct{int i; char c;})-sizeof(int),
X		sizeof(struct{long i; char c;})-sizeof(long));
X
X	/* Pointers *******************************************************/
X	printf("/\* Char pointers = %d bits%s *\/\n", sizeof(char *)*bits,
X		sizeof(char *)>sizeof(int)?" BEWARE! larger than int!":"");
X	printf("/\* Int pointers = %d bits%s *\/\n", sizeof(int *)*bits,
X		sizeof(int *)>sizeof(int)?" BEWARE! larger than int!":"");
X
X	return bits;
X}
X
Xint log(base, x) int base; double x; {
X	int r=0;
X	while (x>=base) { r++; x/=base; }
X	return r;
X}
X
Xint eprop() { /* See if expressions are evaluated in extended precision */
X	int imant;
X	double a, b, base;
X
X	if (setjmp(lab)!=0) { printf("\nUnexpected under/overflow\n"); exit(1); }
X
X	/* Size of mantissa **************************************/
X	a=1.0;
X	do { a=a+a; } while ((((a+1.0)-a)-1.0) == 0.0);
X	b=1.0;
X	do { b=b+b; } while ((base=((a+b)-a)) == 0.0);
X
X	imant=0; b=1.0;
X	do { imant++; b=b*base; }
X	while ((((b+1.0)-b)-1.0) == 0.0);
X	return imant;
X}
X
X#define fabs(x) (((x)<0.0)?(-x):(x))
X
X#endif /* ifndef PASS */
X
X/* As I said, I apologise for the contortions below. The procedures are
X   expanded twice (for float and double) or three times (for short, int and
X   long) by the preprocessor. That way, I never make a change to one that
X   I forget to make to the other. #undef on an already undefined thing
X   is (wrongly) flagged as an error by some compilers, therefore the #ifdef
X   that follows: 
X*/
X
X#ifdef Number
X#undef Number
X#undef THING
X#undef FPROP
X#undef Store
X#undef Sum
X#undef Diff
X#undef Mul
X#undef Div
X#endif
X
X#ifdef Integer
X#undef Integer
X#undef INT
X#undef IPROP
X#endif
X
X#if PASS == 1
X
X#define Number float
X#define THING "float"
X#define FPROP fprop
X#define Store fStore
X#define Sum fSum
X#define Diff fDiff
X#define Mul fMul
X#define Div fDiv
X
X#define Integer short
X#define INT "short"
X#define IPROP sprop
X
X#endif /* PASS == 1 */
X
X#if PASS == 2
X
X#define Number double
X#define THING "double"
X#define FPROP dprop
X#define Store dStore
X#define Sum dSum
X#define Diff dDiff
X#define Mul dMul
X#define Div dDiv
X
X#define Integer int
X#define INT "int"
X#define IPROP iprop
X
X#endif /* if PASS == 2 */
X
X#if PASS == 3
X
X#define Integer long
X#define INT "long"
X#define IPROP lprop
X
X#endif /* if PASS == 3 */
X
XIPROP() {
X	Integer newi, maxi, maxeri;
X	int ibits, ipower, two=2;
X
X	/* Calculate max short/int/long ***********************************/
X	/* Calculate 2**n-1 until overflow - then use the previous value  */
X
X	newi=1; maxi=0;
X
X	if (setjmp(lab)==0)
X		for(ipower=0; newi>maxi; ipower++) {
X			maxi=newi;
X			newi=newi*two+1;
X		}
X
X	/* Now for those daft Cybers: */
X
X	maxeri=0; newi=maxi;
X
X	if (setjmp(lab)==0)
X		for(ibits=ipower; newi>maxeri; ibits++) {
X			maxeri=newi;
X			newi=newi+newi+1;
X		}
X
X	printf("/\* Maximum %s = %ld (= 2**%d-1) *\/\n",
X				INT, (long)maxi, ipower);
X
X	if (maxeri>maxi) {
X		printf("/\* There is a larger %s, %ld (= 2**%d-1), %s *\/\n",
X			INT, (long)maxeri, ibits, 
X			"but only for addition, not multiplication");
X	}
X}
X
X#ifdef Number
X
X/* These routines are intended to defeat any attempt at optimisation */
XStore(a, b) Number a, *b; { *b=a; }
XNumber Sum(a, b) Number a, b; { Number r; Store(a+b, &r); return (r); }
XNumber Diff(a, b) Number a, b; { Number r; Store(a-b, &r); return (r); }
XNumber Mul(a, b) Number a, b; { Number r; Store(a*b, &r); return (r); }
XNumber Div(a, b) Number a, b; { Number r; Store(a/b, &r); return (r); }
X
Xint FPROP(bits) int bits; {
X	/* Properties of floating types, using algorithms by Cody and Waite
X	   from MA Malcolm, as modified by WM Gentleman and SB Marovich.
X	   Further extended by S Pemberton.
X
X	   Returns the number of digits in the fraction.
X	*/
X
X	int i, ibase, iexp, irnd, imant, iz, k, machep, maxexp, minexp,
X	    mx, negeps, mantbits;
X	Number a, b, base, basein, basem1, eps, epsneg, xmax, newxmax,
X	       xmin, xminner, y, y1, z, z1, z2;
X
X	if (setjmp(lab)!=0) { printf("Unexpected over/underflow\n"); exit(1); }
X
X	printf("\n/\* Properties of %s: *\/\n", THING);
X
X	/* Base and size of mantissa **************************************/
X	a=1.0;
X	do { a=Sum(a, a); } while (Diff(Diff(Sum(a, 1.0), a), 1.0) == 0.0);
X	b=1.0;
X	do { b=Sum(b, b); } while ((base=Diff(Sum(a, b), a)) == 0.0);
X	ibase=base;
X	printf("/\* Base = %d *\/\n", ibase);
X
X	imant=0; b=1.0;
X	do { imant++; b=Mul(b, base); }
X	while (Diff(Diff(Sum(b,1.0),b),1.0) == 0.0);
X	printf("/\* Significant base digits = %d %s %d %s *\/\n",
X			imant, "(= at least", log(10, (double)b),
X			"decimal digits)");
X
X	/* Various flavours of epsilon ************************************/
X	basem1=Diff(base,1.0);
X	if (Diff(Sum(a, basem1), a) != 0.0) irnd=1; 
X	else irnd=0;
X
X	negeps=imant+imant;
X	basein=1.0/base;
X	a=1.0;
X	for(i=1; i<=negeps; i++) a*=basein;
X
X	b=a;
X	while (Diff(Diff(1.0, a), 1.0) == 0.0) {
X		a*=base;
X		negeps--;
X	}
X	negeps= -negeps;
X	printf("/\* Smallest x such that 1.0-base**x != 1.0 = %d *\/\n", negeps);
X
X	epsneg=a;
X	if ((ibase!=2) && (irnd==1)) {
X	/*	a=(a*(1.0+a))/(1.0+1.0); => */
X		a=Div(Mul(a, Sum(1.0, a)), Sum(1.0, 1.0));
X	/*	if ((1.0-a)-1.0 != 0.0) epsneg=a; => */
X		if (Diff(Diff(1.0, a), 1.0) != 0.0) epsneg=a;
X	}
X	printf("/\* Small x such that 1.0-x != 1.0 = %g *\/\n", epsneg);
X	/* it may not be the smallest */
X
X	machep= -imant-imant;
X	a=b;
X	while (Diff(Sum(1.0, a), 1.0) == 0.0) { a*=base; machep++; }
X	printf("/\* Smallest x such that 1.0+base**x != 1.0 = %d *\/\n", machep);
X
X	eps=a;
X	if ((ibase!=2) && (irnd==1)) {
X	/*	a=(a*(1.0+a))/(1.0+1.0); => */
X		a=Div(Mul(a, Sum(1.0, a)), Sum(1.0, 1.0));
X	/*	if ((1.0+a)-1.0 != 0.0) eps=a; => */
X		if (Diff(Sum(1.0, a), 1.0) != 0.0) eps=a;
X	}
X	printf("/\* Smallest x such that 1.0+x != 1.0 = %g *\/\n", eps);
X
X	/* Round or chop **************************************************/
X	if (irnd == 1) { printf("/\* Arithmetic rounds *\/\n"); }
X	else { 
X		printf("/\* Arithmetic chops");
X		if (Diff(Mul(Sum(1.0,eps),1.0),1.0) !=  0.0) {
X			printf(" but uses guard digits");
X		}
X		printf(" *\/\n");
X	}
X
X	/* Size of and minimum normalised exponent ************************/
X	y=0; i=0; k=1; z=basein; z1=(1.0+eps)/base;
X
X	/* Coarse search for the largest power of two */
X	if (setjmp(lab)==0) /* in case of underflow trap */
X		do {
X			y=z; y1=z1;
X			z=Mul(y,y); z1=Mul(z1, y);
X			a=Mul(z,1.0);
X			z2=Div(z1,y);
X			if (z2 != y1) break;
X			if ((Sum(a,a) == 0.0) || (fabs(z) >= y)) break;
X			i++;
X			k+=k;
X		} while(1);
X
X	if (ibase != 10) {
X		iexp=i+1; /* for the sign */
X		mx=k+k;
X	} else {
X		iexp=2;
X		iz=ibase;
X		while (k >= iz) { iz*=ibase; iexp++; }
X		mx=iz+iz-1;
X	}
X
X	/* Fine tune starting with y and y1 */
X	if (setjmp(lab)==0) /* in case of underflow trap */
X		do {
X			xmin=y; z1=y1;
X			y=Div(y,base); y1=Div(y1,base);
X			a=Mul(y,1.0);
X			z2=Mul(y1,base);
X			if (z2 != z1) break;
X			if ((Sum(a,a) == 0.0) || (fabs(y) >= xmin)) break;
X			k++;
X		} while (1);
X
X	if (setjmp(lab)!=0) { printf("Unexpected over/underflow\n"); exit(1); }
X
X	minexp=(-k)+1;
X
X	if ((mx <= k+k-3) && (ibase != 10)) { mx+=mx; iexp+=1; }
X	printf("/\* Number of bits used for exponent = %d *\/\n", iexp);
X	printf("/\* Minimum normalised exponent = %d *\/\n", minexp);
X	printf("/\* Minimum normalised positive number = %g *\/\n", xmin);
X
X	/* Minimum exponent ************************************************/
X	if (setjmp(lab)==0) /* in case of underflow trap */
X		do {
X			xminner=y;
X			y=Div(y,base);
X			a=Mul(y,1.0);
X			if ((Sum(a,a) == 0.0) || (fabs(y) >= xminner)) break;
X		} while (1);
X
X	if (setjmp(lab)!=0) { printf("Unexpected over/underflow\n"); exit(1); }
X
X	if (xminner != 0.0 && xminner != xmin) {
X		printf("/\* The smallest numbers are not kept normalised *\/\n");
X		printf("/\* Smallest unnormalised positive number = %g *\/\n",
X			xminner);
X	} else printf("/\* The smallest numbers are normalised *\/\n");
X
X	/* Maximum exponent ************************************************/
X	maxexp=2; xmax=1.0; newxmax=base+1.0;
X	if (setjmp(lab) == 0) {
X		while (xmax<newxmax) {
X			xmax=newxmax;
X			newxmax=Mul(newxmax, base);
X			if (Div(newxmax, base) != xmax) break; /* ieee infinity */
X			maxexp++;
X		}
X	}
X	if (setjmp(lab)!=0) { printf("Unexpected over/underflow\n"); exit(1); }
X
X	printf("/\* Maximum exponent = %d *\/\n", maxexp);
X
X	/* Largest number ***************************************************/
X	xmax=Diff(1.0, epsneg);
X	if (Mul(xmax,1.0) != xmax) xmax=Diff(1.0, Mul(base,epsneg));
X	for (i=1; i<=maxexp; i++) xmax=Mul(xmax, base);
X	printf("/\* Maximum number = %g *\/\n", xmax);
X
X	/* Hidden bit + sanity check ****************************************/
X	if (ibase != 10) {
X		mantbits=log(2, (double)ibase)*imant;
X		if (mantbits+iexp+1 == sizeof(Number)*bits+1) {
X			printf("/\* Arithmetic uses a hidden bit *\/\n");
X		} else if (mantbits+iexp+1 == sizeof(Number)*bits) {
X			printf("/\* Arithmetic doesn't use a hidden bit *\/\n");
X		} else {
X			printf("/\* Something fishy here! %s %s %s *\/\n",
X				"Exponent size + mantissa size doesn't match",
X				"with the size of a", THING);
X		}
X	}
X	return imant;
X}
X
X#endif /* ifdef Number */
X
X#if PASS == 3
X#undef PASS
X#define PASS 4
X#endif
X
X#if PASS == 2
X#undef PASS
X#define PASS 3
X#endif
X
X#if PASS == 1
X#undef PASS
X#define PASS 2
X#endif
X
X#if PASS < 4
X#include __FILE__
X#endif
EOF
echo 'Part 01 out of 01 of pack.out complete.'
exit 0
-- 

Steven Pemberton, CWI, Amsterdam; steven@cwi.nl