?? hard-params.c
字號(hào):
/* Everything you wanted to know about your machine and C compiler, but didn't know who to ask. Author: Steven Pemberton, CWI, Amsterdam; steven@cwi.nl Bugfixes and upgrades gratefully received. Name changed to `hard-params' by Richard Stallman, April 89. xmalloc function defined, Richard Stallman, June 89. Avoid macro in #include, Richard Stallman, Jan 90. Undef CHAR_BIT, etc., if defined in stdio.h, Richard Stallman, Aug 90. In EPROP, don't compare a with old if bad is already set. Stallman, May 91. Don't handle long doubles if -DNO_LONG_DOUBLE. Stallman, May 91. Copyright (c) 1988, 1989 Steven Pemberton, CWI, Amsterdam. All rights reserved. COMPILING With luck and a following wind, just the following will work: cc hard-params.c -o hard-params If your compiler doesn't support: add flag: signed char (eg pcc) -DNO_SC unsigned char -DNO_UC unsigned short and long -DNO_UI signal(), or setjmp/longjmp() -DNO_SIG Try it first with no flags, and see if you get any errors - you might be surprised. (Most non-ANSI compilers need -DNO_SC, though.) Some compilers need a -f flag for floating point. Don't use any optimisation flags: the program may not work if you do. Though "while (a+1.0-a-1.0 == 0.0)" may look like "while(1)" to an optimiser, to a floating-point unit there's a world of difference. Some compilers offer various flags for different floating point modes; it's worth trying all possible combinations of these. Add -DID=\"name\" if you want the machine/flags identified in the output. SYSTEM DEPENDENCIES You may possibly need to add some calls to signal() for other sorts of exception on your machine than SIGFPE, and SIGOVER. See lines beginning #ifdef SIGxxx in main() (and communicate the differences to me!). If your C preprocessor doesn't have the predefined __FILE__ macro, and you want to call this file anything other than hard-params.c, change the #define command for __FILE__ accordingly. If it doesn't accept macro names at all in #include lines, order a new C compiler. While you're waiting for it to arrive, change the last #include in this file (the last but one line) accordingly. OUTPUT Run without argument to get the information as English text. If run with argument -l (e.g. hard-params -l), output is a series of #define's for the ANSI standard limits.h include file, excluding MB_MAX_CHAR. If run with argument -f, output is a series of #define's for the ANSI standard float.h include file. Flag -v gives verbose output: output includes the English text above as C comments. The program exit(0)'s if everything went ok, otherwise it exits with a positive number, telling how many problems there were. VERIFYING THE COMPILER If, having produced the float.h and limits.h header files, you want to verify that the compiler reads them back correctly (there are a lot of boundary cases, of course, like minimum and maximum numbers), you can recompile hard-params.c with -DVERIFY set (plus the other flags that you used when compiling the version that produced the header files). This then recompiles the program so that it #includes "limits.h" and "float.h", and checks that the constants it finds there are the same as the constants it produces. Run the resulting program with hard-params -fl. As of this writing, of 21 compiler/flags combinations only 1 compiler has passed without error! (The honour goes to 'pcc' on an IBM RT.) You can also use this option if your compiler already has both files, and you want to confirm that this program produces the right results. TROUBLE SHOOTING. This program is now quite trustworthy, and suspicious and wrong output may well be caused by bugs in the compiler, not in the program (however of course, this is not guaranteed, and no responsibility can be accepted, etc.) The program only works if overflows are ignored by the C system or are catchable with signal(). If the program fails to run to completion (often with the error message "Unexpected signal at point x"), this often turns out to be a bug in the C compiler's run-time system. Check what was about to be printed, and try to narrow the problem down. Another possible problem is that you have compiled the program to produce loss-of-precision arithmetic traps. The program cannot cope with these, and you should re-compile without them. (They should never be the default). Make sure you compiled with optimisation turned off. Output preceded by *** WARNING: identifies behaviour of the C system deemed incorrect by the program. Likely problems are that printf or scanf don't cope properly with certain boundary numbers. For each float and double that is printed, the printed value is checked that it is correct by using sscanf to read it back. Care is taken that numbers are printed with enough digits to uniquely identify them, and therefore that they can be read back identically. If the number read back is different, the program prints a warning message. If the two numbers in the warning look identical, then printf is more than likely rounding the last digit(s) incorrectly. To put you at ease that the two really are different, the bit patterns of the two numbers are also printed. The difference is very likely in the last bit. Many scanf's read the minimum double back as 0.0, and similarly cause overflow when reading the maximum double. The program quite ruthlessly declares all these behaviours faulty. The warning that "a cast didn't work" refers to cases like this: float f; #define C 1.234567890123456789 f= C; if (f != (float) C) printf ("Wrong!"); A faulty compiler will widen f to double and ignore the cast to float, and because there is more accuracy in a double than a float, fail to recognise that they are the same. In the actual case in point, f and C are passed as parameters to a function that discovers they are not equal, so it's just possible that the error was in the parameter passing, not in the cast (see function Validate()). For ANSI C, which has float constants, the error message is "constant has wrong precision". REPORTING PROBLEMS If the program doesn't work for you for any reason that can't be narrowed down to a problem in the C compiler, or it has to be changed in order to get it to compile, or it produces suspicious output (like a very low maximum float, for instance), please mail the problem and an example of the incorrect output to steven@cwi.nl or mcvax!steven.uucp, so that improvements can be worked into future versions; mcvax/cwi.nl is the European backbone, and is connected to uunet and other fine hosts. This version of the program is the first to try to catch and diagnose bugs in the compiler/run-time system. I would be especially pleased to have reports of failures so that I can improve this service. I apologise unreservedly for the contorted use of the preprocessor... THE SMALL PRINT You may copy and distribute verbatim copies of this source file. You may modify this source file, and copy and distribute such modified versions, provided that you leave the copyright notice at the top of the file and also cause the modified file to carry prominent notices stating that you changed the files and the date of any change; and cause the whole of any work that you distribute or publish, that in whole or in part contains or is a derivative of this program or any part thereof, to be licensed at no charge to all third parties on terms identical to those here. If you do have a fix to any problem, please send it to me, so that other people can have the benefits. While every effort has been taken to make this program as reliable as possible, no responsibility can be taken for the correctness of the output, or suitability for any particular use. ACKNOWLEDGEMENTS Many people have given time and ideas to making this program what it is. To all of them thanks, and apologies for not mentioning them by name.*/#ifndef __FILE__#define __FILE__ "hard-params.c"#endif#ifndef PASS#define PASS 1#define PASS1 1#define VERSION "4.1"/* Procedure just marks the functions that don't return a result */#ifdef Procedure#undef Procedure#endif#define Procedure#define Vprintf if (V) printf/* stdc is used in tests like if (stdc) */#ifdef __STDC__#define stdc 1#else#define stdc 0#endif/* volatile is used to reduce the chance of optimisation, and to prevent variables being put in registers (when setjmp/longjmp wouldn't work as we want) */#ifndef __STDC__#define volatile static#endif#include <stdio.h>/* Kludge around the possiblity that <stdio.h> includes <limits.h> */#ifdef CHAR_BIT#undef CHAR_BIT#undef CHAR_MAX#undef CHAR_MIN#undef SCHAR_MAX#undef SCHAR_MIN#undef UCHAR_MAX#undef UCHAR_MIN#endif#ifdef VERIFY#include "limits.h"#include "float.h"#endif#ifdef NO_SIG /* There's no signal(), or setjmp/longjmp() */ /* Dummy routines instead */ int lab=1; int setjmp(lab) int lab; { return(0); } signal(i, p) int i, (*p)(); {}#else#include <signal.h>#include <setjmp.h> jmp_buf lab; overflow(sig) int sig; { /* what to do on overflow/underflow */ signal(sig, overflow); longjmp(lab, 1); }#endif /*NO_SIG*/#define Unexpected(place) if (setjmp(lab)!=0) croak(place)int V= 0, /* verbose */ L= 0, /* produce limits.h */ F= 0, /* produce float.h */ bugs=0; /* The number of (possible) bugs in the output */char co[4], oc[4]; /* Comment starter and ender symbols */int bits_per_byte; /* the number of bits per unit returned by sizeof() */#ifdef TEST/* Set the fp modes on a SUN with 68881 chip, to check that different rounding modes etc. get properly detected. Compile with additional flag -DTEST, and run with additional parameter +hex-number, to set the 68881 mode register to hex-number*//* Bits 0x30 = rounding mode: */#define ROUND_BITS 0x30#define TO_NEAREST 0x00#define TO_ZERO 0x10#define TO_MINUS_INF 0x20#define TO_PLUS_INF 0x30 /* The SUN FP user's guide seems to be wrong here *//* Bits 0xc0 = extended rounding: */#define EXT_BITS 0xc0#define ROUND_EXTENDED 0x00#define ROUND_SINGLE 0x40#define ROUND_DOUBLE 0x80/* Enabled traps: */#define EXE_INEX1 0x100#define EXE_INEX2 0x200#define EXE_DZ 0x400#define EXE_UNFL 0x800#define EXE_OVFL 0x1000#define EXE_OPERR 0x2000#define EXE_SNAN 0x4000#define EXE_BSUN 0x8000printmode(new) unsigned new; { fpmode_(&new); printf("New fp mode:\n"); printf(" Round toward "); switch (new & ROUND_BITS) { case TO_NEAREST: printf("nearest"); break; case TO_ZERO: printf("zero"); break; case TO_MINUS_INF: printf("minus infinity"); break; case TO_PLUS_INF: printf("plus infinity"); break; default: printf("???"); break; } printf("\n Extended rounding precision: "); switch (new & EXT_BITS) { case ROUND_EXTENDED: printf("extended"); break; case ROUND_SINGLE: printf("single"); break; case ROUND_DOUBLE: printf("double"); break; default: printf("???"); break; } printf("\n Enabled exceptions:"); if (new & (unsigned) EXE_INEX1) printf(" inex1"); if (new & (unsigned) EXE_INEX2) printf(" inex2"); if (new & (unsigned) EXE_DZ) printf(" dz"); if (new & (unsigned) EXE_UNFL) printf(" unfl"); if (new & (unsigned) EXE_OVFL) printf(" ovfl"); if (new & (unsigned) EXE_OPERR) printf(" operr"); if (new & (unsigned) EXE_SNAN) printf(" snan"); if (new & (unsigned) EXE_BSUN) printf(" bsun"); printf("\n");}int setmode(s) char *s; { unsigned mode=0, dig; char c; while (*s) { c= *s++; if (c>='0' && c<='9') dig= c-'0'; else if (c>='a' && c<='f') dig= c-'a'+10; else if (c>='A' && c<='F') dig= c-'A'+10; else return 1; mode= mode<<4 | dig; } printmode(mode); return 0;}#elseint setmode(s) char *s; { fprintf(stderr, "Can't set mode: not compiled with TEST\n"); return(1);}#endifcroak(place) int place; { printf("*** Unexpected signal at point %d\n", place); exit(bugs+1); /* An exit isn't essential here, but avoids loops */}/* This is here in case alloca.c is used. That wants to call this. */char *xmalloc(size) unsigned size; { char *malloc(); char *value = malloc(size); if (value == 0) { fprintf(stderr, "Virtual memory exceeded\n"); exit(bugs+1); } return value;}main(argc, argv) int argc; char *argv[]; { int dprec, fprec, lprec, basic(), fprop(), dprop(), efprop(), edprop(); char *malloc(); unsigned int size; long total; int i; char *s; int bad;#ifdef SIGFPE signal(SIGFPE, overflow);#endif#ifdef SIGOVER signal(SIGOVER, overflow);#endif/* Add more calls as necessary */ Unexpected(1); bad=0; for (i=1; i < argc; i++) { s= argv[i]; if (*s == '-') { s++; while (*s) { switch (*(s++)) { case 'v': V=1; break; case 'l': L=1; break; case 'f': F=1; break; default: bad=1; break; } } } else if (*s == '+') { s++; bad= setmode(s); } else bad= 1; } if (bad) { fprintf(stderr, "Usage: %s [-vlf]\n v=Verbose l=Limits.h f=Float.h\n", argv[0]); exit(1); } if (L || F) { co[0]= '/'; oc[0]= ' '; co[1]= '*'; oc[1]= '*'; co[2]= ' '; oc[2]= '/'; co[3]= '\0'; oc[3]= '\0'; } else { co[0]= '\0'; oc[0]= '\0'; V=1; } if (L) printf("%slimits.h%s\n", co, oc); if (F) printf("%sfloat.h%s\n", co, oc);#ifdef ID printf("%sProduced on %s by hard-params version %s, CWI, Amsterdam%s\n", co, ID, VERSION, oc);#else printf("%sProduced by hard-params version %s, CWI, Amsterdam%s\n", co, VERSION, oc);#endif#ifdef VERIFY printf("%sVerification phase%s\n", co, oc);#endif#ifdef NO_SIG Vprintf("%sCompiled without signal(): %s%s\n", co, "there's nothing that can be done if overflow occurs", oc);#endif#ifdef NO_SC Vprintf("%sCompiled without signed char%s\n", co, oc);#endif#ifdef NO_UC Vprintf("%Compiled without unsigned char%s\n", co, oc);#endif#ifdef NO_UI Vprintf("%Compiled without unsigned short or long%s\n", co, oc);#endif#ifdef __STDC__ Vprintf("%sCompiler claims to be ANSI C level %d%s\n", co, __STDC__, oc);#else Vprintf("%sCompiler does not claim to be ANSI C%s\n", co, oc);
?? 快捷鍵說(shuō)明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -