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