/********************************************************************** * Copyright (c) Digital Equipment Corporation 1984, 1985, 1986. * * All Rights Reserved. * * Reference "/usr/src/COPYRIGHT" for applicable restrictions. * **********************************************************************/ static char Sccsid[] = "@(#)fpx.c 3.0 4/22/86"; /* * ULTRIX-11 Floating Point exerciser program (fpx). * * Fred Canter 5/12/83 * Bill Burns 4/23/84 * added event flag code * * ******************************************** * * * * * This program will not function correctly * * * unless the current unix monitor file is * * * named "unix". * * * * * ******************************************** * * This program exercises the FP11-A/B/C/E/F floating point * processors, and the FPF11 11/23 & 11/24 floating point, * the 11/40 FIS is not supported by Unix. * The fpx program crunches floating point numbers and * tests the floating point exception mechanism. * * USAGE: * * fpx [-n #] [-e #] * * -n # Limit the number of data mismatch errors to be * printed to # for each error occurrence. * * -e # Set the data error limit to #, default = 100 maximum = 1000. * If the number of error occurrences exceeds # terminate fpx. * An error occurrence can represent upto [-n #] error printouts. * */ #include <sys/param.h> /* Does not matter which one ! */ #include <stdio.h> #include <a.out.h> #include <signal.h> #define ITER 99999L long iter = ITER; /* will be lowered for fp emulation */ #define NPASS 12 /* number of forks to make a pass */ int npass = NPASS; /* will be lowered for fp emulation */ int fcount; struct nlist nl[] = { { "fpp" }, { "_fpemulation" }, { "" } }; int fpp; /* floating point present */ int fpx; /* expected FP exception error code, 0 if not */ /* status returned by fperr() system call */ int fpemulation; int fpstat[3]; /* FP status register */ /* FP error code */ /* FP error address */ double a, b; int tn; time_t timbuf; long randx; /* * 16 doubles sorted in assending order, * no magic in these numbers. * Don't use zero !!! */ double n_sort[] = { -6522261.8, -333156.29743, -22416.15, -7522.5482, -321.42, -42.6, -1.1, -0.0021, 0.00014, 2.2, 34.156, 752.8, 9561.42865, 17291.64, 156242.849, 9651422.35, }; /* * Empty array, * a place to put the answer. */ double n_ans[16]; /* * Two arrays of 16 numbers for the * add/subtract test and two arrays of the * expected answers. * again, no magic ! */ double n_as1[] = { 879321.45, -5432543.197, 435678.0001, -1000000.4, 0.00002, -0.12345, 1.9999, -3.459011, 348943.1, -999999.999, 4562999.9003, -9999321.0009, 4589.12006, -43999.00009, 12002.5, -55000.0003, }; double n_as2[] = { 0.29, -1.4, -0.123456, 0.000001, 4.0003, -0.5, -2.25, 0.04999, 3965135.0004, -99368.00032, -54999.9, 99111346.12345, 45832.01, -22479.0245, -4765.1, 345.9999, }; double n_plus[] = { 879321.74, -5432544.597, 435677.876644, -1000000.399999, 4.00032, -0.62345, -0.2501, -3.409021, 4314078.1004, -1099367.99932, 4508000.0003, 89112025.12255, 50421.13006, -66478.02459, 7237.4, -54654.0004 }; double n_min[] = { 879321.16, -5432541.797, 435678.123556, -1000000.400001, -4.00028, 0.37655, 4.2499, -3.509001, -3616191.9004, -900631.99868, 4617999.8003, -109110667.12435, -41242.88994, -21519.97559, 16767.6, -55346.0002 }; char c_exp[100], c_act[100]; /* * Two arrays of 16 numbers for the * multiply/divide test. */ double n_md1[] = { 0.12345, -0.589, 0.00001, -0.6, 12345.0, -4.0, 2001.0, -99000.0, 3896.003, -10999.98, 44367.5489, -2003.478, 1000043.59, -99345.999, 87003.5, -832002.123 }; double n_md2[] = { 0.58999, -0.1, -0.9, 0.3401, 23.0, -567.0, -1234.0, 9.0, 4899.003, -1234.4321, -9984.02, 45903.12, 1.003, -13.6702, -3.002, 5.0034 }; double n_mul[] = { 0.072834, 0.0589, -0.000009, -0.204060, 283935.0, 2268.0, -2469234.0, -891000.0, 19086530.385009, 13578728.411358, -442966495.568578, -91965891.05136, 1003043.72077, 1358079.675530, -261184.507, -4162839.422218 }; double n_div[] = { 0.209241, 5.89, -0.000011, -1.764187, 536.739130, 0.007055, -1.621556, -11000.0, 0.795264, 8.910964, -4.443856, -0.043646, 997052.432702, 7267.340566, -28981.845436, -166287.349203 }; int ndep = 5; int ndrop = 100; int ndec; char *dex = "\n\n[ data error print limit exceeded ]"; int ntec; #ifdef EFLG #include <sys/eflg.h> char *efpis; char *efids; int efbit; int efid; long evntflg(); long efret; int zflag; #else char *killfn = "fpx.kill"; #endif main(argc, argv) char *argv[]; int argc; { int intr(), stop(); register int i, j; int fpxerr(); FILE *argf; long li; int mem; signal(SIGTTOU, SIG_IGN); signal(SIGINT, SIG_IGN); signal(SIGTERM, intr); signal(SIGQUIT, stop); /* if(((argc & 1) == 0) || (argc > 7)) { argerr: fprintf(stderr, "\nfpx: bad arg\n"); fprintf(stderr, "\nUsage: fpx [-n #] [-e #]\n\n"); exit(1); } */ for(i=1; i<argc; i++) { if(argv[i] [0] != '-') { argerr: exit(1); } #ifdef EFLG if(argv[i] [1] == 'z') { zflag++; i++; efpis = argv[i]; i++; efids = argv[i]; } #else if(argv[i] [1] == 'r') { i++; killfn = argv[i]; } #endif if(argv[i] [1] == 'n') { i++; if((argv[i] [0] < '0') || (argv[i] [0] > '9')) goto argerr; ndep = atoi(argv[i]); } if(argv[i] [1] == 'e') { i++; if((argv[i] [0] < '0') || (argv[i] [0] > '9')) goto argerr; ndrop = atoi(argv[i]); if((ndrop < 0) || (ndrop > 1000)) { fprintf(stderr, "\nfpx bad [-e #], using 100 !\n"); ndrop = 100; } } } if(!zflag) { if(isatty(2)) { fprintf(stderr, "fpx: detaching... type \"sysxstop\" to stop\n"); fflush(stderr); } if((i = fork()) == -1) { printf("fpx: Can't fork new copy !\n"); exit(1); } if(i != 0) exit(0); } setpgrp(0, 31111); nlist("/unix", nl); if(nl[1].n_value == 0) { fprintf(stderr,"\nfpx: ULTRIX-11 not configured for Floating Point\n"); exit(1); } if((mem = open("/dev/mem", 0)) < 0) { fprintf(stderr, "\nfpx: Can't open /dev/mem\n"); exit(1); } lseek(mem, (long)nl[0].n_value, 0); read(mem, (char *)&fpp, sizeof(fpp)); lseek(mem, (long)nl[1].n_value, 0); read(mem, (char *)&fpemulation, sizeof(fpemulation)); close(mem); if(fpp == 0) { iter = 999; npass = 6; } if((fpp == 0) && (fpemulation != 1)) { fprintf(stderr, "\nfpx: No Floating Point present, "); if(fpemulation == 2) fprintf(stderr, "and FP Emulation not in kernel !\n"); else fprintf(stderr, "and FP Emulation turned off !\n"); exit(0); } time(&timbuf); randx = timbuf & 0777; printf("\n\nFloating Point exerciser started - %s", ctime(&timbuf)); fflush(stdout); #ifdef EFLG if(zflag) { efbit = atoi(efpis); efid = atoi(efids); evntflg(EFCLR, efid, (long)efbit); } #else unlink(killfn); /* tell sysx fpx started */ #endif signal(SIGFPE, fpxerr); loop: /* * TEST 1 - increment/decrement * * Two floating point numbers starting at 0.0 are incremented * by 0.1 iter (see above define) times. Each time they are * compared to make sure they match. They are then decremented * by 0.1 iter times and checked for a match each time, * and check for a 0.0 result after all iterations are done. * * note - The checking of a & b for >= 0.00001 is really a test * for equal to zero, because with floating point numbers * zero is not always really zero when it gets out the the * Nth decimal place. */ tn = 1; ndec = 0; a = 0.0; b = 0.0; for(li=0; li<iter; li++) { a += 1.00001; b += 1.00001; if((a != b) || (a < 0.00001) || (b < 0.00001)) { em(tn); printf("\nIncrement a & b by 1.00001, %D times,", iter); printf("\na & b should match"); printf("\ncount\t= %D", li); printf("\na\t= %f\nb\t= %f\n", a, b); if(++ndec >= ndep) { printf("%s", dex); break; } } } if(ndec) ntec++; ndec = 0; for(li=0; li<iter; li++) { if((a < 0.00001) || (b < 0.00001)) goto err1; a -= 1.00001; b -= 1.00001; if(a != b) { err1: em(tn); printf("\nDecrement a & b by 1.00001, %D times,", iter); printf("\na & b should match"); printf("\ncount\t= %D", li); printf("\na\t= %f\nb\t= %f\n", a, b); if(++ndec >= ndep) { printf("%s", dex); break; } } } if(ndec) ntec++; if((a >= 0.00001) || (b >= 0.00001)) { em(tn); printf("\nAfter test %02d, a & b should equal zero", tn); printf("\na\t= %f\nb\t= %f\n",a,b); ntec++; } elcheck(); /* check for error limit exceeded */ /* * TEST 2 - exceptions * * Test as many of the floating point exceptions as possible * by attempting illegal operations like divide by zero and * catching the SIGFPE signal. The fperr() system call is used * to obtain the floating point exception status. * NOTE - As it turns out, divide by zero is the only floating * point exception that can be tested. */ tn = 2; fpx = 4; /* divide by zero */ a = 1.1/0.0; fpxck(); elcheck(); /* * TEST 3 - sort * * Copy a presorted array of 16 numbers to an empty array * in randum order, then sort them and compare the result * against the original array. */ tn = 3; ndec = 0; for(i=0; i<16; i++) n_ans[i] = 0.0; /* answer set to all zeroes */ for(i=0; i<16; i++) { /* copy to answer, out of order */ j = rng() & 017; while(n_ans[j] != 0.0) if(++j >= 16) j = 0; n_ans[j] = n_sort[i]; } t3_sl: /* sort */ j = 0; for(i=0; i<15; i++) if(n_ans[i] > n_ans[i+1]) { j++; a = n_ans[i]; n_ans[i] = n_ans[i+1]; n_ans[i+1] = a; } if(j) goto t3_sl; j = 0; /* compare */ for(i=0; i<16; i++) if(n_sort[i] != n_ans[i]) j++; if(j) { /* print any errors */ em(tn); printf("\nSort 16 numbers, arrays should match"); printf("\n\nExpected\tActual\n"); for(i=0; i<16; i++) { printf("\n%15.6f\t%15.6f", n_sort[i], n_ans[i]); if(++ndec >= ndep) { printf("%s", dex); break; } } printf("\n"); } if(ndec) ntec++; elcheck(); /* * TEST 4 - add * * Add two arrays of 16 numbers and * check the result against an array * of the expected answers. * Strings are used to compare the results of * floating point arithmetic operations because of the * inaccuracy of the FP11 in the Nth decimal place. * Printf rounds the results for me. * "Oh silly me !" */ tn = 4; ndec = 0; for(i=0; i<16; i++) { n_ans[i] = n_as1[i] + n_as2[i]; sprintf(&c_exp, "%.6f", n_plus[i]); sprintf(&c_act, "%.6f", n_ans[i]); if(strcmp(&c_exp, &c_act) != 0) { em(tn); printf("\nAddition test"); printf("\n\n%.6f plus %.7f = ", n_as1[i], n_as2[i]); printf("%s S/B %s\n", &c_act, &c_exp); if(++ndec >= ndep) { printf("%s", dex); break; } } } if(ndec) ntec++; elcheck(); /* * TEST 5 - subtract * * Subtract two arrays of 16 numbers and * check the result against an array * of the expected answers. * Strings are used to compare the results of * floating point arithmetic operations because of the * inaccuracy of the FP11 in the Nth decimal place. * Printf rounds the results for me. */ tn = 5; ndec = 0; for(i=0; i<16; i++) { n_ans[i] = n_as1[i] - n_as2[i]; sprintf(&c_exp, "%.6f", n_min[i]); sprintf(&c_act, "%.6f", n_ans[i]); if(strcmp(&c_exp, &c_act) != 0) { em(tn); printf("\nSubtraction test"); printf("\n\n%.6f minus %.7f = ", n_as1[i], n_as2[i]); printf("%s S/B %s\n", &c_act, &c_exp); if(++ndec >= ndep) { printf("%s", dex); break; } } } if(ndec) ntec++; elcheck(); /* * TEST 6 - multiply * * Multiply two arrays of 16 numbers and * check the result against an array * of the expected answers. * Strings are used to compare the results of * floating point arithmetic operations because of the * inaccuracy of the FP11 in the Nth decimal place. * Printf rounds the results for me. */ tn = 6; ndec = 0; for(i=0; i<16; i++) { n_ans[i] = n_md1[i] * n_md2[i]; sprintf(&c_exp, "%.6f", n_mul[i]); sprintf(&c_act, "%.6f", n_ans[i]); if(strcmp(&c_exp, &c_act) != 0) { em(tn); printf("\nMultiplication test"); printf("\n\n%.6f * %.7f = ", n_md1[i], n_md2[i]); printf("%s S/B %s\n", &c_act, &c_exp); if(++ndec >= ndep) { printf("%s", dex); break; } } } if(ndec) ntec++; elcheck(); /* * TEST 7 - divide * * Divide two arrays of 16 numbers and * check the result against an array * of the expected answers. * Strings are used to compare the results of * floating point arithmetic operations because of the * inaccuracy of the FP11 in the Nth decimal place. * Printf rounds the results for me. */ tn = 7; ndec = 0; for(i=0; i<16; i++) { n_ans[i] = n_md1[i] / n_md2[i]; sprintf(&c_exp, "%.6f", n_div[i]); sprintf(&c_act, "%.6f", n_ans[i]); if(strcmp(&c_exp, &c_act) != 0) { em(tn); printf("\nDivision test"); printf("\n\n%.6f / %.7f = ", n_md1[i], n_md2[i]); printf("%s S/B %s\n", &c_act, &c_exp); if(++ndec >= ndep) { printf("%s", dex); break; } } } if(ndec) ntec++; elcheck(); if(++fcount >= npass) { /* end of pass */ fcount = 0; time(&timbuf); printf("\nFloating Point exerciser end of pass - %s", ctime(&timbuf)); } fflush(stdout); if((i = fork()) == 0) goto loop; if(i == -1) { fprintf(stderr, "\nfpx: Can't fork new copy of fpx !\n"); goto loop; } exit(0); } intr() { signal(SIGTERM, intr); #ifdef EFLG if(zflag) { if(!checkflg()) return; } else return; #else if(access(killfn, 0) != 0) return; #endif stop(); } stop() { signal(SIGTERM, SIG_IGN); signal(SIGQUIT, SIG_IGN); time(&timbuf); printf("\n\nFloating Point exerciser stopped - %s\n", ctime(&timbuf)); fflush(stdout); #ifdef EFLG if(zflag) evntflg(EFCLR, efid, (long)efbit); #else unlink(killfn); /* tell SYSX fpx stopped */ #endif exit(0); } rng() { return(((randx = randx * 1103515245 + 12345) >> 16) & 0177777); } em(tn) { printf("\n\n****** FPP EXERCISER ERROR ******"); printf("\n****** TEST NUMBER %02d ******", tn); } /* * Floating point exception error codes. */ char *fpxec[] = { "", "Floating OP code error", "Floating divide by zero", "Floating (or double) to integer conversion error", "Floating overflow", "Floating underflow", "Floating undefined variable", "Maintenance trap", }; /* * Floating point status register bits. */ char *fpsr[] = { "FER","FID","","","FIUV","FIU","FIV","FIC", "FD","FL","FT","FMM","FN","FZ","FV","FC", }; /* * Floating point exception handler. * The variable fpx is set to the error code if * an exception is expected or zero if not. */ fpxerr() { register int i; fperr(&fpstat); /* get FP exception status */ if(fpx && (fpx == fpstat[1])) goto xit; /* expected exception occurred */ ntec++; printf("\n****** Test Number %02d - ", tn); if(fpx == 0) printf("UNEXPECTED "); else printf("INCORRECT "); printf("Floating Point Exception ******"); if(fpx) { printf("\nExpected - %s", fpxec[fpx >> 1]); printf("\nReceived - "); if((fpstat[1] == 0) || (fpstat[1] > 14)) printf("Unknown exception"); else printf("%s", fpxec[fpstat[1] >> 1]); } printf("\n\nFP status register\t%6o\t", fpstat[0]); for(i=0; i<16; i++) if(fpstat[0] & (1 << (15 - i))) printf("%s ", fpsr[i]); printf("\nFP error code\t\t%6o\t", fpstat[1]); if((fpstat[1] == 0) || (fpstat[1] > 14)) printf("Unknown exception"); else printf("%s", fpxec[fpstat[1] >> 1]); printf("\nFP error address\t%6o\n", fpstat[2]); xit: fpx = 0; signal(SIGFPE, fpxerr); } /* * Make sure that the floating * point exception did occur. */ fpxck() { if(fpx == 0) return; printf("\n\n****** Test number %02d - ", tn); printf("MISSED Floating Point Exception ******"); printf("\nExpected - %s", fpxec[fpx >> 1]); printf("\nReceived - No Floating Point exception\n"); ntec++; } elcheck() { #ifdef EFLG if(zflag) { if(checkflg()) stop(); } #else if(access("fpx.kill", 0) == 0) stop(); #endif if(ntec >= ndrop) { printf("\nTotal error limit exceeded, FPX terminated !\n"); fflush(stdout); for( ;; ) sleep(3600); } } /* * Check eventflags to stop * return 0 for continuation * return 1 to stop */ extern int errno; checkflg() { union efrt { long efret; struct { int a; int b; } retval } ef; errno = 0; ef.efret = evntflg(EFRD, efid, (long)0); if(errno && ef.retval.a == -1) { zflag = 0; return(0); } if(ef.efret & (1L << efbit)) return(1); return(0); }