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