Home | History | Annotate | Download | only in perf
      1 // This small program does some raytracing.  It tests Valgrind's handling of
      2 // FP operations.  It apparently does a lot of trigonometry operations.
      3 
      4 // Licensing: This program is closely based on the one of the same name from
      5 // http://www.fourmilab.ch/.  The front page of that site says:
      6 //
      7 //   "Except for a few clearly-marked exceptions, all the material on this
      8 //   site is in the public domain and may be used in any manner without
      9 //   permission, restriction, attribution, or compensation."
     10 
     11 /* This program can be used in two ways.  If INTRIG is undefined, sin,
     12    cos, tan, etc, will be used as supplied by <math.h>.  If it is
     13    defined, then the program calculates all this stuff from first
     14    principles (so to speak) and does not use the libc facilities.  For
     15    benchmarking purposes it seems better to avoid the libc stuff, so
     16    that the inner loops (sin, sqrt) present a workload independent of
     17    libc implementations on different platforms.  Hence: */
     18 
     19 #define INTRIG 1
     20 
     21 
     22 /*
     23 
     24         John Walker's Floating Point Benchmark, derived from...
     25 
     26 	Marinchip Interactive Lens Design System
     27 
     28 				     John Walker   December 1980
     29 
     30 	By John Walker
     31 	   http://www.fourmilab.ch/
     32 
     33 	This  program may be used, distributed, and modified freely as
     34 	long as the origin information is preserved.
     35 
     36 	This  is  a  complete  optical	design	raytracing  algorithm,
     37 	stripped of its user interface and recast into portable C.  It
     38 	not only determines execution speed on an  extremely  floating
     39 	point	(including   trig   function)	intensive   real-world
     40 	application, it  checks  accuracy  on  an  algorithm  that  is
     41 	exquisitely  sensitive	to  errors.   The  performance of this
     42 	program is typically far more  sensitive  to  changes  in  the
     43 	efficiency  of	the  trigonometric  library  routines than the
     44 	average floating point program.
     45 
     46 	The benchmark may be compiled in two  modes.   If  the	symbol
     47 	INTRIG	is  defined,  built-in	trigonometric  and square root
     48 	routines will be used for all calculations.  Timings made with
     49         INTRIG  defined  reflect  the  machine's  basic floating point
     50 	performance for the arithmetic operators.  If  INTRIG  is  not
     51 	defined,  the  system  library	<math.h>  functions  are used.
     52         Results with INTRIG not defined reflect the  system's  library
     53 	performance  and/or  floating  point hardware support for trig
     54 	functions and square root.  Results with INTRIG defined are  a
     55 	good  guide  to  general  floating  point  performance,  while
     56 	results with INTRIG undefined indicate the performance	of  an
     57 	application which is math function intensive.
     58 
     59 	Special  note  regarding  errors in accuracy: this program has
     60 	generated numbers identical to the last digit it  formats  and
     61 	checks on the following machines, floating point
     62 	architectures, and languages:
     63 
     64 	Marinchip 9900	  QBASIC    IBM 370 double-precision (REAL * 8) format
     65 
     66 	IBM PC / XT / AT  Lattice C IEEE 64 bit, 80 bit temporaries
     67 			  High C    same, in line 80x87 code
     68                           BASICA    "Double precision"
     69 			  Quick BASIC IEEE double precision, software routines
     70 
     71 	Sun 3		  C	    IEEE 64 bit, 80 bit temporaries,
     72 				    in-line 68881 code, in-line FPA code.
     73 
     74         MicroVAX II       C         Vax "G" format floating point
     75 
     76 	Macintosh Plus	  MPW C     SANE floating point, IEEE 64 bit format
     77 				    implemented in ROM.
     78 
     79 	Inaccuracies  reported	by  this  program should be taken VERY
     80 	SERIOUSLY INDEED, as the program has been demonstrated	to  be
     81 	invariant  under  changes in floating point format, as long as
     82 	the format is a recognised double precision  format.   If  you
     83 	encounter errors, please remember that they are just as likely
     84 	to  be	in  the  floating  point  editing   library   or   the
     85 	trigonometric  libraries  as  in  the low level operator code.
     86 
     87 	The benchmark assumes that results are basically reliable, and
     88 	only tests the last result computed against the reference.  If
     89         you're running on  a  suspect  system  you  can  compile  this
     90 	program  with  ACCURACY defined.  This will generate a version
     91 	which executes as an infinite loop, performing the  ray  trace
     92 	and checking the results on every pass.  All incorrect results
     93 	will be reported.
     94 
     95 	Representative	timings  are  given  below.   All  have   been
     96 	normalised as if run for 1000 iterations.
     97 
     98   Time in seconds		   Computer, Compiler, and notes
     99  Normal      INTRIG
    100 
    101  3466.00    4031.00	Commodore 128, 2 Mhz 8510 with software floating
    102 			point.	Abacus Software/Data-Becker Super-C 128,
    103 			version 3.00, run in fast (2 Mhz) mode.  Note:
    104 			the results generated by this system differed
    105 			from the reference results in the 8th to 10th
    106 			decimal place.
    107 
    108  3290.00		IBM PC/AT 6 Mhz, Microsoft/IBM BASICA version A3.00.
    109                         Run with the "/d" switch, software floating point.
    110 
    111  2131.50		IBM PC/AT 6 Mhz, Lattice C version 2.14, small model.
    112 			This version of Lattice compiles subroutine
    113 			calls which either do software floating point
    114 			or use the 80x87.  The machine on which I ran
    115 			this had an 80287, but the results were so bad
    116 			I wonder if it was being used.
    117 
    118  1598.00		Macintosh Plus, MPW C, SANE Software floating point.
    119 
    120  1582.13		Marinchip 9900 2 Mhz, QBASIC compiler with software
    121 			floating point.  This was a QBASIC version of the
    122 			program which contained the identical algorithm.
    123 
    124   404.00		IBM PC/AT 6 Mhz, Microsoft QuickBASIC version 2.0.
    125 			Software floating point.
    126 
    127   165.15		IBM PC/AT 6 Mhz, Metaware High C version 1.3, small
    128 			model.	This was compiled to call subroutines for
    129 			floating point, and the machine contained an 80287
    130 			which was used by the subroutines.
    131 
    132   143.20		Macintosh II, MPW C, SANE calls.  I was unable to
    133 			determine whether SANE was using the 68881 chip or
    134 			not.
    135 
    136   121.80		Sun 3/160 16 Mhz, Sun C.  Compiled with -fsoft switch
    137 			which executes floating point in software.
    138 
    139    78.78     110.11	IBM RT PC (Model 6150).  IBM AIX 1.0 C compiler
    140 			with -O switch.
    141 
    142    75.2      254.0	Microsoft Quick C 1.0, in-line 8087 instructions,
    143 			compiled with 80286 optimisation on.  (Switches
    144 			were -Ol -FPi87-G2 -AS).  Small memory model.
    145 
    146    69.50		IBM PC/AT 6Mhz, Borland Turbo BASIC 1.0.  Compiled
    147                         in "8087 required" mode to generate in-line
    148 			code for the math coprocessor.
    149 
    150    66.96		IBM PC/AT 6Mhz, Microsoft QuickBASIC 4.0.  This
    151 			release of QuickBASIC compiles code for the
    152 			80287 math coprocessor.
    153 
    154    66.36     206.35	IBM PC/AT 6Mhz, Metaware High C version 1.3, small
    155 			model.	This was compiled with in-line code for the
    156 			80287 math coprocessor.  Trig functions still call
    157 			library routines.
    158 
    159    63.07     220.43	IBM PC/AT, 6Mhz, Borland Turbo C, in-line 8087 code,
    160 			small model, word alignment, no stack checking,
    161 			8086 code mode.
    162 
    163    17.18		Apollo DN-3000, 12 Mhz 68020 with 68881, compiled
    164 			with in-line code for the 68881 coprocessor.
    165 			According to Apollo, the library routines are chosen
    166 			at runtime based on coprocessor presence.  Since the
    167 			coprocessor was present, the library is supposed to
    168 			use in-line floating point code.
    169 
    170    15.55      27.56	VAXstation II GPX.  Compiled and executed under
    171 			VAX/VMS C.
    172 
    173    15.14      37.93	Macintosh II, Unix system V.  Green Hills 68020
    174 			Unix compiler with in-line code for the 68881
    175 			coprocessor (-O -ZI switches).
    176 
    177    12.69		Sun 3/160 16 Mhz, Sun C.  Compiled with -fswitch,
    178 			which calls a subroutine to select the fastest
    179 			floating point processor.  This was using the 68881.
    180 
    181    11.74      26.73	Compaq Deskpro 386, 16 Mhz 80386 with 16 Mhz 80387.
    182 			Metaware High C version 1.3, compiled with in-line
    183 			for the math coprocessor (but not optimised for the
    184 			80386/80387).  Trig functions still call library
    185 			routines.
    186 
    187     8.43      30.49	Sun 3/160 16 Mhz, Sun C.  Compiled with -f68881,
    188 			generating in-line MC68881 instructions.  Trig
    189 			functions still call library routines.
    190 
    191     6.29      25.17	Sun 3/260 25 Mhz, Sun C.  Compiled with -f68881,
    192 			generating in-line MC68881 instructions.  Trig
    193 			functions still call library routines.
    194 
    195     4.57		Sun 3/260 25 Mhz, Sun FORTRAN 77.  Compiled with
    196 			-O -f68881, generating in-line MC68881 instructions.
    197 			Trig functions are compiled in-line.  This used
    198 			the FORTRAN 77 version of the program, FBFORT77.F.
    199 
    200     4.00      14.20	Sun386i/25 Mhz model 250, Sun C compiler.
    201 
    202     4.00      14.00	Sun386i/25 Mhz model 250, Metaware C.
    203 
    204     3.10      12.00	Compaq 386/387 25 Mhz running SCO Xenix 2.
    205 			Compiled with Metaware HighC 386, optimized
    206 			for 386.
    207 
    208     3.00      12.00	Compaq 386/387 25MHZ optimized for 386/387.
    209 
    210     2.96       5.17	Sun 4/260, Sparc RISC processor.  Sun C,
    211 			compiled with the -O2 switch for global
    212 			optimisation.
    213 
    214     2.47		COMPAQ 486/25, secondary cache disabled, High C,
    215 			486/387, inline f.p., small memory model.
    216 
    217     2.20       3.40	Data General Motorola 88000, 16 Mhz, Gnu C.
    218 
    219     1.56		COMPAQ 486/25, 128K secondary cache, High C, 486/387,
    220 			inline f.p., small memory model.
    221 
    222     0.66       1.50	DEC Pmax, Mips processor.
    223 
    224     0.63       0.91	Sun SparcStation 2, Sun C (SunOS 4.1.1) with
    225                         -O4 optimisation and "/usr/lib/libm.il" inline
    226 			floating point.
    227 
    228     0.60       1.07	Intel 860 RISC processor, 33 Mhz, Greenhills
    229 			C compiler.
    230 
    231     0.40       0.90	Dec 3MAX, MIPS 3000 processor, -O4.
    232 
    233     0.31       0.90	IBM RS/6000, -O.
    234 
    235     0.1129     0.2119	Dell Dimension XPS P133c, Pentium 133 MHz,
    236 			Windows 95, Microsoft Visual C 5.0.
    237 
    238     0.0883     0.2166	Silicon Graphics Indigo, MIPS R4400,
    239                         175 Mhz, "-O3".
    240 
    241     0.0351     0.0561	Dell Dimension XPS R100, Pentium II 400 MHz,
    242 			Windows 98, Microsoft Visual C 5.0.
    243 
    244     0.0312     0.0542	Sun Ultra 2, UltraSPARC V9, 300 MHz, Solaris
    245 			2.5.1.
    246 
    247     0.00862    0.01074  Dell Inspiron 9100, Pentium 4, 3.4 GHz, gcc -O3.
    248 
    249 */
    250 
    251 
    252 #include <stdio.h>
    253 #include <stdlib.h>
    254 #include <string.h>
    255 #ifndef INTRIG
    256 #include <math.h>
    257 #endif
    258 
    259 #define cot(x) (1.0 / tan(x))
    260 
    261 #define TRUE  1
    262 #define FALSE 0
    263 
    264 #define max_surfaces 10
    265 
    266 /*  Local variables  */
    267 
    268 /* static char tbfr[132]; */
    269 
    270 static short current_surfaces;
    271 static short paraxial;
    272 
    273 static double clear_aperture;
    274 
    275 static double aberr_lspher;
    276 static double aberr_osc;
    277 static double aberr_lchrom;
    278 
    279 static double max_lspher;
    280 static double max_osc;
    281 static double max_lchrom;
    282 
    283 static double radius_of_curvature;
    284 static double object_distance;
    285 static double ray_height;
    286 static double axis_slope_angle;
    287 static double from_index;
    288 static double to_index;
    289 
    290 static double spectral_line[9];
    291 static double s[max_surfaces][5];
    292 static double od_sa[2][2];
    293 
    294 static char outarr[8][80];	   /* Computed output of program goes here */
    295 
    296 int itercount;			   /* The iteration counter for the main loop
    297 				      in the program is made global so that
    298 				      the compiler should not be allowed to
    299 				      optimise out the loop over the ray
    300 				      tracing code. */
    301 
    302 #ifndef ITERATIONS
    303 #define ITERATIONS /*1000*/ /*500000*/ 80000
    304 #endif
    305 int niter = ITERATIONS; 	   /* Iteration counter */
    306 
    307 static char *refarr[] = {	   /* Reference results.  These happen to
    308 				      be derived from a run on Microsoft
    309 				      Quick BASIC on the IBM PC/AT. */
    310 
    311         "   Marginal ray          47.09479120920   0.04178472683",
    312         "   Paraxial ray          47.08372160249   0.04177864821",
    313         "Longitudinal spherical aberration:        -0.01106960671",
    314         "    (Maximum permissible):                 0.05306749907",
    315         "Offense against sine condition (coma):     0.00008954761",
    316         "    (Maximum permissible):                 0.00250000000",
    317         "Axial chromatic aberration:                0.00448229032",
    318         "    (Maximum permissible):                 0.05306749907"
    319 };
    320 
    321 /* The	test  case  used  in  this program is the  design for a 4 inch
    322    achromatic telescope  objective  used  as  the  example  in  Wyld's
    323    classic  work  on  ray  tracing by hand, given in Amateur Telescope
    324    Making, Volume 3.  */
    325 
    326 static double testcase[4][4] = {
    327 	{27.05, 1.5137, 63.6, 0.52},
    328 	{-16.68, 1, 0, 0.138},
    329 	{-16.68, 1.6164, 36.7, 0.38},
    330 	{-78.1, 1, 0, 0}
    331 };
    332 
    333 /*  Internal trig functions (used only if INTRIG is  defined).	 These
    334     standard  functions  may be enabled to obtain timings that reflect
    335     the machine's floating point performance rather than the speed  of
    336     its trig function evaluation.  */
    337 
    338 #ifdef INTRIG
    339 
    340 /*  The following definitions should keep you from getting intro trouble
    341     with compilers which don't let you redefine intrinsic functions.  */
    342 
    343 #define sin I_sin
    344 #define cos I_cos
    345 #define tan I_tan
    346 #define sqrt I_sqrt
    347 #define atan I_atan
    348 #define atan2 I_atan2
    349 #define asin I_asin
    350 
    351 #define fabs(x)  ((x < 0.0) ? -x : x)
    352 
    353 #define pic 3.1415926535897932
    354 
    355 /*  Commonly used constants  */
    356 
    357 static double pi = pic,
    358 	twopi =pic * 2.0,
    359 	piover4 = pic / 4.0,
    360 	fouroverpi = 4.0 / pic,
    361 	piover2 = pic / 2.0;
    362 
    363 /*  Coefficients for ATAN evaluation  */
    364 
    365 static double atanc[] = {
    366 	0.0,
    367 	0.4636476090008061165,
    368 	0.7853981633974483094,
    369 	0.98279372324732906714,
    370 	1.1071487177940905022,
    371 	1.1902899496825317322,
    372 	1.2490457723982544262,
    373 	1.2924966677897852673,
    374 	1.3258176636680324644
    375 };
    376 
    377 /*  aint(x)	  Return integer part of number.  Truncates towards 0	 */
    378 
    379 double aint(x)
    380 double x;
    381 {
    382 	long l;
    383 
    384 	/*  Note that this routine cannot handle the full floating point
    385 	    number range.  This function should be in the machine-dependent
    386 	    floating point library!  */
    387 
    388 	l = x;
    389 	if ((int)(-0.5) != 0  &&  l < 0 )
    390 	   l++;
    391 	x = l;
    392 	return x;
    393 }
    394 
    395 /*  sin(x)	  Return sine, x in radians  */
    396 
    397 static double sin(x)
    398 double x;
    399 {
    400 	int sign;
    401 	double y, r, z;
    402 
    403 	x = (((sign= (x < 0.0)) != 0) ? -x: x);
    404 
    405 	if (x > twopi)
    406 	   x -= (aint(x / twopi) * twopi);
    407 
    408 	if (x > pi) {
    409 	   x -= pi;
    410 	   sign = !sign;
    411 	}
    412 
    413 	if (x > piover2)
    414 	   x = pi - x;
    415 
    416 	if (x < piover4) {
    417 	   y = x * fouroverpi;
    418 	   z = y * y;
    419 	   r = y * (((((((-0.202253129293E-13 * z + 0.69481520350522E-11) * z -
    420 	      0.17572474176170806E-8) * z + 0.313361688917325348E-6) * z -
    421 	      0.365762041821464001E-4) * z + 0.249039457019271628E-2) * z -
    422 	      0.0807455121882807815) * z + 0.785398163397448310);
    423 	} else {
    424 	   y = (piover2 - x) * fouroverpi;
    425 	   z = y * y;
    426 	   r = ((((((-0.38577620372E-12 * z + 0.11500497024263E-9) * z -
    427 	      0.2461136382637005E-7) * z + 0.359086044588581953E-5) * z -
    428 	      0.325991886926687550E-3) * z + 0.0158543442438154109) * z -
    429 	      0.308425137534042452) * z + 1.0;
    430 	}
    431 	return sign ? -r : r;
    432 }
    433 
    434 /*  cos(x)	  Return cosine, x in radians, by identity  */
    435 
    436 static double cos(x)
    437 double x;
    438 {
    439 	x = (x < 0.0) ? -x : x;
    440 	if (x > twopi)		      /* Do range reduction here to limit */
    441 	   x = x - (aint(x / twopi) * twopi); /* roundoff on add of PI/2    */
    442 	return sin(x + piover2);
    443 }
    444 
    445 /*  tan(x)	  Return tangent, x in radians, by identity  */
    446 
    447 static double tan(x)
    448 double x;
    449 {
    450 	return sin(x) / cos(x);
    451 }
    452 
    453 /*  sqrt(x)	  Return square root.  Initial guess, then Newton-
    454 		  Raphson refinement  */
    455 
    456 double sqrt(x)
    457 double x;
    458 {
    459 	double c, cl, y;
    460 	int n;
    461 
    462 	if (x == 0.0)
    463 	   return 0.0;
    464 
    465 	if (x < 0.0) {
    466 	   fprintf(stderr,
    467               "\nGood work!  You tried to take the square root of %g",
    468 	     x);
    469 	   fprintf(stderr,
    470               "\nunfortunately, that is too complex for me to handle.\n");
    471 	   exit(1);
    472 	}
    473 
    474 	y = (0.154116 + 1.893872 * x) / (1.0 + 1.047988 * x);
    475 
    476 	c = (y - x / y) / 2.0;
    477 	cl = 0.0;
    478 	for (n = 50; c != cl && n--;) {
    479 	   y = y - c;
    480 	   cl = c;
    481 	   c = (y - x / y) / 2.0;
    482 	}
    483 	return y;
    484 }
    485 
    486 /*  atan(x)	  Return arctangent in radians,
    487 		  range -pi/2 to pi/2  */
    488 
    489 static double atan(x)
    490 double x;
    491 {
    492 	int sign, l, y;
    493 	double a, b, z;
    494 
    495 	x = (((sign = (x < 0.0)) != 0) ? -x : x);
    496 	l = 0;
    497 
    498 	if (x >= 4.0) {
    499 	   l = -1;
    500 	   x = 1.0 / x;
    501 	   y = 0;
    502 	   goto atl;
    503 	} else {
    504 	   if (x < 0.25) {
    505 	      y = 0;
    506 	      goto atl;
    507 	   }
    508 	}
    509 
    510 	y = aint(x / 0.5);
    511 	z = y * 0.5;
    512 	x = (x - z) / (x * z + 1);
    513 
    514 atl:
    515 	z = x * x;
    516 	b = ((((893025.0 * z + 49116375.0) * z + 425675250.0) * z +
    517 	    1277025750.0) * z + 1550674125.0) * z + 654729075.0;
    518 	a = (((13852575.0 * z + 216602100.0) * z + 891080190.0) * z +
    519 	    1332431100.0) * z + 654729075.0;
    520 	a = (a / b) * x + atanc[y];
    521 	if (l)
    522 	   a=piover2 - a;
    523 	return sign ? -a : a;
    524 }
    525 
    526 /*  atan2(y,x)	  Return arctangent in radians of y/x,
    527 		  range -pi to pi  */
    528 
    529 static double atan2(y, x)
    530 double y, x;
    531 {
    532 	double temp;
    533 
    534 	if (x == 0.0) {
    535 	   if (y == 0.0)   /*  Special case: atan2(0,0) = 0  */
    536 	      return 0.0;
    537 	   else if (y > 0)
    538 	      return piover2;
    539 	   else
    540 	      return -piover2;
    541 	}
    542 	temp = atan(y / x);
    543 	if (x < 0.0) {
    544 	   if (y >= 0.0)
    545 	      temp += pic;
    546 	   else
    547 	      temp -= pic;
    548 	}
    549 	return temp;
    550 }
    551 
    552 /*  asin(x)	  Return arcsine in radians of x  */
    553 
    554 static double asin(x)
    555 double x;
    556 {
    557 	if (fabs(x)>1.0) {
    558 	   fprintf(stderr,
    559               "\nInverse trig functions lose much of their gloss when");
    560 	   fprintf(stderr,
    561               "\ntheir arguments are greater than 1, such as the");
    562 	   fprintf(stderr,
    563               "\nvalue %g you passed.\n", x);
    564 	   exit(1);
    565 	}
    566 	return atan2(x, sqrt(1 - x * x));
    567 }
    568 #endif
    569 
    570 /*	      Calculate passage through surface
    571 
    572 	      If  the variable PARAXIAL is true, the trace through the
    573 	      surface will be done using the paraxial  approximations.
    574 	      Otherwise,  the normal trigonometric trace will be done.
    575 
    576 	      This routine takes the following inputs:
    577 
    578 	      RADIUS_OF_CURVATURE	  Radius of curvature of surface
    579 					  being crossed.  If 0, surface is
    580 					  plane.
    581 
    582 	      OBJECT_DISTANCE		  Distance of object focus from
    583 					  lens vertex.	If 0, incoming
    584 					  rays are parallel and
    585 					  the following must be specified:
    586 
    587 	      RAY_HEIGHT		  Height of ray from axis.  Only
    588 					  relevant if OBJECT.DISTANCE == 0
    589 
    590 	      AXIS_SLOPE_ANGLE		  Angle incoming ray makes with axis
    591 					  at intercept
    592 
    593 	      FROM_INDEX		  Refractive index of medium being left
    594 
    595 	      TO_INDEX			  Refractive index of medium being
    596 					  entered.
    597 
    598 	      The outputs are the following variables:
    599 
    600 	      OBJECT_DISTANCE		  Distance from vertex to object focus
    601 					  after refraction.
    602 
    603 	      AXIS_SLOPE_ANGLE		  Angle incoming ray makes with axis
    604 					  at intercept after refraction.
    605 
    606 */
    607 
    608 static void transit_surface() {
    609 	double iang,		   /* Incidence angle */
    610 	       rang,		   /* Refraction angle */
    611 	       iang_sin,	   /* Incidence angle sin */
    612 	       rang_sin,	   /* Refraction angle sin */
    613 	       old_axis_slope_angle, sagitta;
    614 
    615 	if (paraxial) {
    616 	   if (radius_of_curvature != 0.0) {
    617 	      if (object_distance == 0.0) {
    618 		 axis_slope_angle = 0.0;
    619 		 iang_sin = ray_height / radius_of_curvature;
    620 	      } else
    621 		 iang_sin = ((object_distance -
    622 		    radius_of_curvature) / radius_of_curvature) *
    623 		    axis_slope_angle;
    624 
    625 	      rang_sin = (from_index / to_index) *
    626 		 iang_sin;
    627 	      old_axis_slope_angle = axis_slope_angle;
    628 	      axis_slope_angle = axis_slope_angle +
    629 		 iang_sin - rang_sin;
    630 	      if (object_distance != 0.0)
    631 		 ray_height = object_distance * old_axis_slope_angle;
    632 	      object_distance = ray_height / axis_slope_angle;
    633 	      return;
    634 	   }
    635 	   object_distance = object_distance * (to_index / from_index);
    636 	   axis_slope_angle = axis_slope_angle * (from_index / to_index);
    637 	   return;
    638 	}
    639 
    640 	if (radius_of_curvature != 0.0) {
    641 	   if (object_distance == 0.0) {
    642 	      axis_slope_angle = 0.0;
    643 	      iang_sin = ray_height / radius_of_curvature;
    644 	   } else {
    645 	      iang_sin = ((object_distance -
    646 		 radius_of_curvature) / radius_of_curvature) *
    647 		 sin(axis_slope_angle);
    648 	   }
    649 	   iang = asin(iang_sin);
    650 	   rang_sin = (from_index / to_index) *
    651 	      iang_sin;
    652 	   old_axis_slope_angle = axis_slope_angle;
    653 	   axis_slope_angle = axis_slope_angle +
    654 	      iang - asin(rang_sin);
    655 	   sagitta = sin((old_axis_slope_angle + iang) / 2.0);
    656 	   sagitta = 2.0 * radius_of_curvature*sagitta*sagitta;
    657 	   object_distance = ((radius_of_curvature * sin(
    658 	      old_axis_slope_angle + iang)) *
    659 	      cot(axis_slope_angle)) + sagitta;
    660 	   return;
    661 	}
    662 
    663 	rang = -asin((from_index / to_index) *
    664 	   sin(axis_slope_angle));
    665 	object_distance = object_distance * ((to_index *
    666 	   cos(-rang)) / (from_index *
    667 	   cos(axis_slope_angle)));
    668 	axis_slope_angle = -rang;
    669 }
    670 
    671 /*  Perform ray trace in specific spectral line  */
    672 
    673 static void trace_line(line, ray_h)
    674 int line;
    675 double ray_h;
    676 {
    677 	int i;
    678 
    679 	object_distance = 0.0;
    680 	ray_height = ray_h;
    681 	from_index = 1.0;
    682 
    683 	for (i = 1; i <= current_surfaces; i++) {
    684 	   radius_of_curvature = s[i][1];
    685 	   to_index = s[i][2];
    686 	   if (to_index > 1.0)
    687 	      to_index = to_index + ((spectral_line[4] -
    688 		 spectral_line[line]) /
    689 		 (spectral_line[3] - spectral_line[6])) * ((s[i][2] - 1.0) /
    690 		 s[i][3]);
    691 	   transit_surface();
    692 	   from_index = to_index;
    693 	   if (i < current_surfaces)
    694 	      object_distance = object_distance - s[i][4];
    695 	}
    696 }
    697 
    698 /*  Initialise when called the first time  */
    699 
    700 int main(argc, argv)
    701 int argc;
    702 char *argv[];
    703 {
    704 	int i, j, k, errors;
    705 	double od_fline, od_cline;
    706 #ifdef ACCURACY
    707 	long passes;
    708 #endif
    709 
    710 	spectral_line[1] = 7621.0;	 /* A */
    711 	spectral_line[2] = 6869.955;	 /* B */
    712 	spectral_line[3] = 6562.816;	 /* C */
    713 	spectral_line[4] = 5895.944;	 /* D */
    714 	spectral_line[5] = 5269.557;	 /* E */
    715 	spectral_line[6] = 4861.344;	 /* F */
    716         spectral_line[7] = 4340.477;     /* G'*/
    717 	spectral_line[8] = 3968.494;	 /* H */
    718 
    719 	/* Process the number of iterations argument, if one is supplied. */
    720 
    721 	if (argc > 1) {
    722 	   niter = atoi(argv[1]);
    723            if (*argv[1] == '-' || niter < 1) {
    724               printf("This is John Walker's floating point accuracy and\n");
    725               printf("performance benchmark program.  You call it with\n");
    726               printf("\nfbench <itercount>\n\n");
    727               printf("where <itercount> is the number of iterations\n");
    728               printf("to be executed.  Archival timings should be made\n");
    729               printf("with the iteration count set so that roughly five\n");
    730               printf("minutes of execution is timed.\n");
    731 	      exit(0);
    732 	   }
    733 	}
    734 
    735 	/* Load test case into working array */
    736 
    737 	clear_aperture = 4.0;
    738 	current_surfaces = 4;
    739 	for (i = 0; i < current_surfaces; i++)
    740 	   for (j = 0; j < 4; j++)
    741 	      s[i + 1][j + 1] = testcase[i][j];
    742 
    743 #ifdef ACCURACY
    744         printf("Beginning execution of floating point accuracy test...\n");
    745 	passes = 0;
    746 #else
    747         printf("Ready to begin John Walker's floating point accuracy\n");
    748         printf("and performance benchmark.  %d iterations will be made.\n\n",
    749 	   niter);
    750 
    751         printf("\nMeasured run time in seconds should be divided by %.f\n", niter / 1000.0);
    752         printf("to normalise for reporting results.  For archival results,\n");
    753         printf("adjust iteration count so the benchmark runs about five minutes.\n\n");
    754 
    755         //printf("Press return to begin benchmark:");
    756 	//gets(tbfr);
    757 #endif
    758 
    759 	/* Perform ray trace the specified number of times. */
    760 
    761 #ifdef ACCURACY
    762 	while (TRUE) {
    763 	   passes++;
    764 	   if ((passes % 100L) == 0) {
    765               printf("Pass %ld.\n", passes);
    766 	   }
    767 #else
    768 	for (itercount = 0; itercount < niter; itercount++) {
    769 #endif
    770 
    771 	   for (paraxial = 0; paraxial <= 1; paraxial++) {
    772 
    773 	      /* Do main trace in D light */
    774 
    775 	      trace_line(4, clear_aperture / 2.0);
    776 	      od_sa[paraxial][0] = object_distance;
    777 	      od_sa[paraxial][1] = axis_slope_angle;
    778 	   }
    779 	   paraxial = FALSE;
    780 
    781 	   /* Trace marginal ray in C */
    782 
    783 	   trace_line(3, clear_aperture / 2.0);
    784 	   od_cline = object_distance;
    785 
    786 	   /* Trace marginal ray in F */
    787 
    788 	   trace_line(6, clear_aperture / 2.0);
    789 	   od_fline = object_distance;
    790 
    791 	   aberr_lspher = od_sa[1][0] - od_sa[0][0];
    792 	   aberr_osc = 1.0 - (od_sa[1][0] * od_sa[1][1]) /
    793 	      (sin(od_sa[0][1]) * od_sa[0][0]);
    794 	   aberr_lchrom = od_fline - od_cline;
    795 	   max_lspher = sin(od_sa[0][1]);
    796 
    797 	   /* D light */
    798 
    799 	   max_lspher = 0.0000926 / (max_lspher * max_lspher);
    800 	   max_osc = 0.0025;
    801 	   max_lchrom = max_lspher;
    802 #ifndef ACCURACY
    803 	}
    804 
    805         //printf("Stop the timer:\007");
    806 	//gets(tbfr);
    807 #endif
    808 
    809 	/* Now evaluate the accuracy of the results from the last ray trace */
    810 
    811         sprintf(outarr[0], "%15s   %21.11f  %14.11f",
    812            "Marginal ray", od_sa[0][0], od_sa[0][1]);
    813         sprintf(outarr[1], "%15s   %21.11f  %14.11f",
    814            "Paraxial ray", od_sa[1][0], od_sa[1][1]);
    815 	sprintf(outarr[2],
    816            "Longitudinal spherical aberration:      %16.11f",
    817 	   aberr_lspher);
    818 	sprintf(outarr[3],
    819            "    (Maximum permissible):              %16.11f",
    820 	   max_lspher);
    821 	sprintf(outarr[4],
    822            "Offense against sine condition (coma):  %16.11f",
    823 	   aberr_osc);
    824 	sprintf(outarr[5],
    825            "    (Maximum permissible):              %16.11f",
    826 	   max_osc);
    827 	sprintf(outarr[6],
    828            "Axial chromatic aberration:             %16.11f",
    829 	   aberr_lchrom);
    830 	sprintf(outarr[7],
    831            "    (Maximum permissible):              %16.11f",
    832 	   max_lchrom);
    833 
    834 	/* Now compare the edited results with the master values from
    835 	   reference executions of this program. */
    836 
    837 	errors = 0;
    838 	for (i = 0; i < 8; i++) {
    839 	   if (strcmp(outarr[i], refarr[i]) != 0) {
    840 #ifdef ACCURACY
    841               printf("\nError in pass %ld for results on line %d...\n",
    842 		     passes, i + 1);
    843 #else
    844               printf("\nError in results on line %d...\n", i + 1);
    845 #endif
    846               printf("Expected:  \"%s\"\n", refarr[i]);
    847               printf("Received:  \"%s\"\n", outarr[i]);
    848               printf("(Errors)    ");
    849 	      k = strlen(refarr[i]);
    850 	      for (j = 0; j < k; j++) {
    851                  printf("%c", refarr[i][j] == outarr[i][j] ? ' ' : '^');
    852 		 if (refarr[i][j] != outarr[i][j])
    853 		    errors++;
    854 	      }
    855               printf("\n");
    856 	   }
    857 	}
    858 #ifdef ACCURACY
    859 	}
    860 #else
    861 	if (errors > 0) {
    862            printf("\n%d error%s in results.  This is VERY SERIOUS.\n",
    863               errors, errors > 1 ? "s" : "");
    864 	} else
    865            printf("\nNo errors in results.\n");
    866 #endif
    867 	return 0;
    868 }
    869