📄 paranoia.c
字号:
#undef V9#define NOPAUSE/* A C version of Kahan's Floating Point Test "Paranoia" Thos Sumner, UCSF, Feb. 1985 David Gay, BTL, Jan. 1986 This is a rewrite from the Pascal version by B. A. Wichmann, 18 Jan. 1985 (and does NOT exhibit good C programming style).(C) Apr 19 1983 in BASIC version by: Professor W. M. Kahan, 567 Evans Hall Electrical Engineering & Computer Science Dept. University of California Berkeley, California 94720 USAconverted to Pascal by: B. A. Wichmann National Physical Laboratory Teddington Middx TW11 OLW UKconverted to C by: David M. Gay and Thos Sumner AT&T Bell Labs Computer Center, Rm. U-76 600 Mountain Avenue University of California Murray Hill, NJ 07974 San Francisco, CA 94143 USA USAwith simultaneous corrections to the Pascal source (reflectedin the Pascal source available over netlib).[A couple of bug fixes from dgh = sun!dhough incorporated 31 July 1986.]Reports of results on various systems from all the versionsof Paranoia are being collected by Richard Karpinski at thesame address as Thos Sumner. This includes sample outputs,bug reports, and criticisms.You may copy this program freely if you acknowledge its source.Comments on the Pascal version to NPL, please.The C version catches signals from floating-point exceptions.If signal(SIGFPE,...) is unavailable in your environment, you may#define NOSIGNAL to comment out the invocations of signal.This source file is too big for some C compilers, but may be splitinto pieces. Comments containing "SPLIT" suggest convenient placesfor this splitting. At the end of these comments is an "ed script"(for the UNIX(tm) editor ed) that will do this splitting.By #defining Single when you compile this source, you may obtaina single-precision C version of Paranoia.The following is from the introductory commentary from Wichmann's work:The BASIC program of Kahan is written in Microsoft BASIC using manyfacilities which have no exact analogy in Pascal. The Pascalversion below cannot therefore be exactly the same. Rather than bea minimal transcription of the BASIC program, the Pascal codingfollows the conventional style of block-structured languages. Hencethe Pascal version could be useful in producing versions in otherstructured languages.Rather than use identifiers of minimal length (which therefore havelittle mnemonic significance), the Pascal version uses meaningfulidentifiers as follows [Note: A few changes have been made for C]:BASIC C BASIC C BASIC C A J S StickyBit A1 AInverse J0 NoErrors T B Radix [Failure] T0 Underflow B1 BInverse J1 NoErrors T2 ThirtyTwo B2 RadixD2 [SeriousDefect] T5 OneAndHalf B9 BMinusU2 J2 NoErrors T7 TwentySeven C [Defect] T8 TwoForty C1 CInverse J3 NoErrors U OneUlp D [Flaw] U0 UnderflowThreshold D4 FourD K PageNo U1 E0 L Milestone U2 E1 M V E2 Exp2 N V0 E3 N1 V8 E5 MinSqEr O Zero V9 E6 SqEr O1 One W E7 MaxSqEr O2 Two X E8 O3 Three X1 E9 O4 Four X8 F1 MinusOne O5 Five X9 Random1 F2 Half O8 Eight Y F3 Third O9 Nine Y1 F6 P Precision Y2 F9 Q Y9 Random2 G1 GMult Q8 Z G2 GDiv Q9 Z0 PseudoZero G3 GAddSub R Z1 H R1 RMult Z2 H1 HInverse R2 RDiv Z9 I R3 RAddSub IO NoTrials R4 RSqrt I3 IEEE R9 Random9 SqRWrngAll the variables in BASIC are true variables and in consequence,the program is more difficult to follow since the "constants" mustbe determined (the glossary is very helpful). The Pascal versionuses Real constants, but checks are added to ensure that the valuesare correctly converted by the compiler.The major textual change to the Pascal version apart from theidentifiersis that named procedures are used, inserting parameterswherehelpful. New procedures are also introduced. Thecorrespondence is as follows:BASIC Pascallines 90- 140 Pause 170- 250 Instructions 380- 460 Heading 480- 670 Characteristics 690- 870 History2940-2950 Random3710-3740 NewD4040-4080 DoesYequalX4090-4110 PrintIfNPositive4640-4850 TestPartialUnderflow=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=Below is an "ed script" that splits para.c into 10 filesof the form part[1-8].c, subs.c, and msgs.c, plus a headerfile, paranoia.h, that these files require.r paranoia.c$?SPLIT+,$w msgs.c .,$d?SPLIT .d+d-,$w subs.c-,$d?part8+d?include .,$w part8.c .,$d-d?part7+d?include .,$w part7.c .,$d-d?part6+d?include .,$w part6.c .,$d-d?part5+d?include .,$w part5.c .,$d-d?part4+d?include .,$w part4.c .,$d-d?part3+d?include .,$w part3.c .,$d-d?part2+d?include .,$w part2.c .,$d?SPLIT .d1,/^#include/-1d1,$w part1.c/Computed constants/,$d1,$s/^int/extern &/1,$s/^FLOAT/extern &/1,$s/^char/extern &/1,$s! = .*!;!/^Guard/,/^Round/s/^/extern //^jmp_buf/s/^/extern //^Sig_type/s/^/extern /s/$/\extern void sigfpe();/w paranoia.hq*/#include <stdio.h>#ifndef NOSIGNAL#include <signal.h>#endif#include <setjmp.h>extern double fabs(), floor(), log(), pow(), sqrt();#ifdef Single#define FLOAT float#define FABS(x) (float)fabs((double)(x))#define FLOOR(x) (float)floor((double)(x))#define LOG(x) (float)log((double)(x))#define POW(x,y) (float)pow((double)(x),(double)(y))#define SQRT(x) (float)sqrt((double)(x))#else#define FLOAT double#define FABS(x) fabs(x)#define FLOOR(x) floor(x)#define LOG(x) log(x)#define POW(x,y) pow(x,y)#define SQRT(x) sqrt(x)#endifjmp_buf ovfl_buf;typedef void (*Sig_type)();Sig_type sigsave;#define KEYBOARD 0FLOAT Radix, BInvrse, RadixD2, BMinusU2;FLOAT Sign(), Random();/*Small floating point constants.*/FLOAT Zero = 0.0;FLOAT Half = 0.5;FLOAT One = 1.0;FLOAT Two = 2.0;FLOAT Three = 3.0;FLOAT Four = 4.0;FLOAT Five = 5.0;FLOAT Eight = 8.0;FLOAT Nine = 9.0;FLOAT TwentySeven = 27.0;FLOAT ThirtyTwo = 32.0;FLOAT TwoForty = 240.0;FLOAT MinusOne = -1.0;FLOAT OneAndHalf = 1.5;/*Integer constants*/int NoTrials = 20; /*Number of tests for commutativity. */#define False 0#define True 1/* Definitions for declared types Guard == (Yes, No); Rounding == (Chopped, Rounded, Other); Message == packed array [1..40] of char; Class == (Flaw, Defect, Serious, Failure); */#define Yes 1#define No 0#define Chopped 2#define Rounded 1#define Other 0#define Flaw 3#define Defect 2#define Serious 1#define Failure 0typedef int Guard, Rounding, Class;typedef char Message;/* Declarations of Variables */int Indx;char ch[8];FLOAT AInvrse, A1;FLOAT C, CInvrse;FLOAT D, FourD;FLOAT E0, E1, Exp2, E3, MinSqEr;FLOAT SqEr, MaxSqEr, E9;FLOAT Third;FLOAT F6, F9;FLOAT H, HInvrse;int I;FLOAT StickyBit, J;FLOAT MyZero;FLOAT Precision;FLOAT Q, Q9;FLOAT R, Random9;FLOAT T, Underflow, S;FLOAT OneUlp, UfThold, U1, U2;FLOAT V, V0, V9;FLOAT W;FLOAT X, X1, X2, X8, Random1;FLOAT Y, Y1, Y2, Random2;FLOAT Z, PseudoZero, Z1, Z2, Z9;int ErrCnt[4];int fpecount;int Milestone;int PageNo;int M, N, N1;Guard GMult, GDiv, GAddSub;Rounding RMult, RDiv, RAddSub, RSqrt;int Break, Done, NotMonot, Monot, Anomaly, IEEE, SqRWrng, UfNGrad;/* Computed constants. *//*U1 gap below 1.0, i.e, 1.0-U1 is next number below 1.0 *//*U2 gap above 1.0, i.e, 1.0+U2 is next number above 1.0 *//* floating point exception receiver */ voidsigfpe(i){ fpecount++; printf("\n* * * FLOATING-POINT ERROR * * *\n"); fflush(stdout); if (sigsave) {#ifndef NOSIGNAL signal(SIGFPE, sigsave);#endif sigsave = 0; longjmp(ovfl_buf, 1); } abort();}main(){#ifdef mc char *out; ieee_flags("set", "precision", "double", &out);#endif /* First two assignments use integer right-hand sides. */ Zero = 0; One = 1; Two = One + One; Three = Two + One; Four = Three + One; Five = Four + One; Eight = Four + Four; Nine = Three * Three; TwentySeven = Nine * Three; ThirtyTwo = Four * Eight; TwoForty = Four * Five * Three * Four; MinusOne = -One; Half = One / Two; OneAndHalf = One + Half; ErrCnt[Failure] = 0; ErrCnt[Serious] = 0; ErrCnt[Defect] = 0; ErrCnt[Flaw] = 0; PageNo = 1; /*=============================================*/ Milestone = 0; /*=============================================*/#ifndef NOSIGNAL signal(SIGFPE, sigfpe);#endif Instructions(); Pause(); Heading(); Pause(); Characteristics(); Pause(); History(); Pause(); /*=============================================*/ Milestone = 7; /*=============================================*/ printf("Program is now RUNNING tests on small integers:\n"); TstCond (Failure, (Zero + Zero == Zero) && (One - One == Zero) && (One > Zero) && (One + One == Two), "0+0 != 0, 1-1 != 0, 1 <= 0, or 1+1 != 2"); Z = - Zero; if (Z != 0.0) { ErrCnt[Failure] = ErrCnt[Failure] + 1; printf("Comparison alleges that -0.0 is Non-zero!\n"); U1 = 0.001; Radix = 1; TstPtUf(); } TstCond (Failure, (Three == Two + One) && (Four == Three + One) && (Four + Two * (- Two) == Zero) && (Four - Three - One == Zero), "3 != 2+1, 4 != 3+1, 4+2*(-2) != 0, or 4-3-1 != 0"); TstCond (Failure, (MinusOne == (0 - One)) && (MinusOne + One == Zero ) && (One + MinusOne == Zero) && (MinusOne + FABS(One) == Zero) && (MinusOne + MinusOne * MinusOne == Zero), "-1+1 != 0, (-1)+abs(1) != 0, or -1+(-1)*(-1) != 0"); TstCond (Failure, Half + MinusOne + Half == Zero, "1/2 + (-1) + 1/2 != 0"); /*=============================================*/ /*SPLIT part2(); part3(); part4(); part5(); part6(); part7(); part8(); }#include "paranoia.h"part2(){*/ Milestone = 10; /*=============================================*/ TstCond (Failure, (Nine == Three * Three) && (TwentySeven == Nine * Three) && (Eight == Four + Four) && (ThirtyTwo == Eight * Four) && (ThirtyTwo - TwentySeven - Four - One == Zero), "9 != 3*3, 27 != 9*3, 32 != 8*4, or 32-27-4-1 != 0"); TstCond (Failure, (Five == Four + One) && (TwoForty == Four * Five * Three * Four) && (TwoForty / Three - Four * Four * Five == Zero) && ( TwoForty / Four - Five * Three * Four == Zero) && ( TwoForty / Five - Four * Three * Four == Zero), "5 != 4+1, 240/3 != 80, 240/4 != 60, or 240/5 != 48"); if (ErrCnt[Failure] == 0) { printf("-1, 0, 1/2, 1, 2, 3, 4, 5, 9, 27, 32 & 240 are O.K.\n"); printf("\n"); } printf("Searching for Radix and Precision.\n"); W = One; do { W = W + W; Y = W + One; Z = Y - W; Y = Z - One; } while (MinusOne + FABS(Y) < Zero); /*.. now W is just big enough that |((W+1)-W)-1| >= 1 ...*/ Precision = Zero; Y = One; do { Radix = W + Y; Y = Y + Y; Radix = Radix - W; } while ( Radix == Zero); if (Radix < Two) Radix = One; printf("Radix = %f .\n", Radix); if (Radix != 1) { W = One; do { Precision = Precision + One; W = W * Radix; Y = W + One; } while ((Y - W) == One); } /*... now W == Radix^Precision is barely too big to satisfy (W+1)-W == 1 ...*/ U1 = One / W; U2 = Radix * U1; printf("Closest relative separation found is U1 = %.7e .\n\n", U1); printf("Recalculating radix and precision\n "); /*save old values*/ E0 = Radix; E1 = U1; E9 = U2; E3 = Precision; X = Four / Three; Third = X - One; F6 = Half - Third; X = F6 + F6; X = FABS(X - Third); if (X < U2) X = U2; /*... now X = (unknown no.) ulps of 1+...*/ do { U2 = X; Y = Half * U2 + ThirtyTwo * U2 * U2; Y = One + Y; X = Y - One; } while ( ! ((U2 <= X) || (X <= Zero))); /*... now U2 == 1 ulp of 1 + ... */ X = Two / Three; F6 = X - Half; Third = F6 + F6; X = Third - Half; X = FABS(X + F6); if (X < U1) X = U1; /*... now X == (unknown no.) ulps of 1 -... */ do { U1 = X; Y = Half * U1 + ThirtyTwo * U1 * U1; Y = Half - Y; X = Half + Y; Y = Half - X; X = Half + Y; } while ( ! ((U1 <= X) || (X <= Zero))); /*... now U1 == 1 ulp of 1 - ... */ if (U1 == E1) printf("confirms closest relative separation U1 .\n"); else printf("gets better closest relative separation U1 = %.7e .\n", U1); W = One / U1; F9 = (Half - U1) + Half; Radix = FLOOR(0.01 + U2 / U1); if (Radix == E0) printf("Radix confirmed.\n"); else printf("MYSTERY: recalculated Radix = %.7e .\n", Radix); TstCond (Defect, Radix <= Eight + Eight, "Radix is too big: roundoff problems"); TstCond (Flaw, (Radix == Two) || (Radix == 10) || (Radix == One), "Radix is not as good as 2 or 10"); /*=============================================*/ Milestone = 20; /*=============================================*/ TstCond (Failure, F9 - Half < Half, "(1-U1)-1/2 < 1/2 is FALSE, prog. fails?"); X = F9; I = 1; Y = X - Half; Z = Y - Half; TstCond (Failure, (X != One) || (Z == Zero), "Comparison is fuzzy,X=1 but X-1/2-1/2 != 0"); X = One + U2; I = 0; /*=============================================*/ Milestone = 25; /*=============================================*/ /*... BMinusU2 = nextafter(Radix, 0) */ BMinusU2 = Radix - One; BMinusU2 = (BMinusU2 - U2) + One; /* Purify Integers */ if (Radix != One) { X = - TwoForty * LOG(U1) / LOG(Radix); Y = FLOOR(Half + X); if (FABS(X - Y) * Four < One) X = Y; Precision = X / TwoForty; Y = FLOOR(Half + Precision); if (FABS(Precision - Y) * TwoForty < Half) Precision = Y; } if ((Precision != FLOOR(Precision)) || (Radix == One)) { printf("Precision cannot be characterized by an Integer number\n");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -