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