1 2 // This is slightly hacked version of perf/fbench.c. It does some 3 // some basic FP arithmetic (+/-/*/divide) and not a lot else. 4 5 // This small program does some raytracing. It tests Valgrind's handling of 6 // FP operations. It apparently does a lot of trigonometry operations. 7 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: 10 // 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." 14 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: */ 22 23 #define INTRIG 1 24 25 26 /* 27 28 John Walker's Floating Point Benchmark, derived from... 29 30 Marinchip Interactive Lens Design System 31 32 John Walker December 1980 33 34 By John Walker 35 http://www.fourmilab.ch/ 36 37 This program may be used, distributed, and modified freely as 38 long as the origin information is preserved. 39 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. 49 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. 62 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: 67 68 Marinchip 9900 QBASIC IBM 370 double-precision (REAL * 8) format 69 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 74 75 Sun 3 C IEEE 64 bit, 80 bit temporaries, 76 in-line 68881 code, in-line FPA code. 77 78 MicroVAX II C Vax "G" format floating point 79 80 Macintosh Plus MPW C SANE floating point, IEEE 64 bit format 81 implemented in ROM. 82 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. 90 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. 98 99 Representative timings are given below. All have been 100 normalised as if run for 1000 iterations. 101 102 Time in seconds Computer, Compiler, and notes 103 Normal INTRIG 104 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. 111 112 3290.00 IBM PC/AT 6 Mhz, Microsoft/IBM BASICA version A3.00. 113 Run with the "/d" switch, software floating point. 114 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. 121 122 1598.00 Macintosh Plus, MPW C, SANE Software floating point. 123 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. 127 128 404.00 IBM PC/AT 6 Mhz, Microsoft QuickBASIC version 2.0. 129 Software floating point. 130 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. 135 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. 139 140 121.80 Sun 3/160 16 Mhz, Sun C. Compiled with -fsoft switch 141 which executes floating point in software. 142 143 78.78 110.11 IBM RT PC (Model 6150). IBM AIX 1.0 C compiler 144 with -O switch. 145 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. 149 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. 153 154 66.96 IBM PC/AT 6Mhz, Microsoft QuickBASIC 4.0. This 155 release of QuickBASIC compiles code for the 156 80287 math coprocessor. 157 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. 162 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. 166 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. 173 174 15.55 27.56 VAXstation II GPX. Compiled and executed under 175 VAX/VMS C. 176 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). 180 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. 184 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. 190 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. 194 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. 198 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. 203 204 4.00 14.20 Sun386i/25 Mhz model 250, Sun C compiler. 205 206 4.00 14.00 Sun386i/25 Mhz model 250, Metaware C. 207 208 3.10 12.00 Compaq 386/387 25 Mhz running SCO Xenix 2. 209 Compiled with Metaware HighC 386, optimized 210 for 386. 211 212 3.00 12.00 Compaq 386/387 25MHZ optimized for 386/387. 213 214 2.96 5.17 Sun 4/260, Sparc RISC processor. Sun C, 215 compiled with the -O2 switch for global 216 optimisation. 217 218 2.47 COMPAQ 486/25, secondary cache disabled, High C, 219 486/387, inline f.p., small memory model. 220 221 2.20 3.40 Data General Motorola 88000, 16 Mhz, Gnu C. 222 223 1.56 COMPAQ 486/25, 128K secondary cache, High C, 486/387, 224 inline f.p., small memory model. 225 226 0.66 1.50 DEC Pmax, Mips processor. 227 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. 231 232 0.60 1.07 Intel 860 RISC processor, 33 Mhz, Greenhills 233 C compiler. 234 235 0.40 0.90 Dec 3MAX, MIPS 3000 processor, -O4. 236 237 0.31 0.90 IBM RS/6000, -O. 238 239 0.1129 0.2119 Dell Dimension XPS P133c, Pentium 133 MHz, 240 Windows 95, Microsoft Visual C 5.0. 241 242 0.0883 0.2166 Silicon Graphics Indigo, MIPS R4400, 243 175 Mhz, "-O3". 244 245 0.0351 0.0561 Dell Dimension XPS R100, Pentium II 400 MHz, 246 Windows 98, Microsoft Visual C 5.0. 247 248 0.0312 0.0542 Sun Ultra 2, UltraSPARC V9, 300 MHz, Solaris 249 2.5.1. 250 251 0.00862 0.01074 Dell Inspiron 9100, Pentium 4, 3.4 GHz, gcc -O3. 252 253 */ 254 255 256 #include <stdio.h> 257 #include <stdlib.h> 258 #include <string.h> 259 #ifndef INTRIG 260 #include <math.h> 261 #endif 262 263 #define cot(x) (1.0 / tan(x)) 264 265 #define TRUE 1 266 #define FALSE 0 267 268 #define max_surfaces 10 269 270 /* Local variables */ 271 272 /* static char tbfr[132]; */ 273 274 static short current_surfaces; 275 static short paraxial; 276 277 static double clear_aperture; 278 279 static double aberr_lspher; 280 static double aberr_osc; 281 static double aberr_lchrom; 282 283 static double max_lspher; 284 static double max_osc; 285 static double max_lchrom; 286 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; 293 294 static double spectral_line[9]; 295 static double s[max_surfaces][5]; 296 static double od_sa[2][2]; 297 298 static char outarr[8][80]; /* Computed output of program goes here */ 299 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. */ 305 306 #ifndef ITERATIONS 307 #define ITERATIONS /*1000*/ /*500000*/ 100 308 #endif 309 int niter = ITERATIONS; /* Iteration counter */ 310 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. */ 314 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" 323 }; 324 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. */ 329 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} 335 }; 336 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. */ 341 342 #ifdef INTRIG 343 344 /* The following definitions should keep you from getting intro trouble 345 with compilers which don't let you redefine intrinsic functions. */ 346 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 354 355 #define fabs(x) ((x < 0.0) ? -x : x) 356 357 #define pic 3.1415926535897932 358 359 /* Commonly used constants */ 360 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; 366 367 /* Coefficients for ATAN evaluation */ 368 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 379 }; 380 381 /* aint(x) Return integer part of number. Truncates towards 0 */ 382 383 double aint(x) 384 double x; 385 { 386 long l; 387 388 /* Note that this routine cannot handle the full floating point 389 number range. This function should be in the machine-dependent 390 floating point library! */ 391 392 l = x; 393 if ((int)(-0.5) != 0 && l < 0 ) 394 l++; 395 x = l; 396 return x; 397 } 398 399 /* sin(x) Return sine, x in radians */ 400 401 static double sin(x) 402 double x; 403 { 404 int sign; 405 double y, r, z; 406 407 x = (((sign= (x < 0.0)) != 0) ? -x: x); 408 409 if (x > twopi) 410 x -= (aint(x / twopi) * twopi); 411 412 if (x > pi) { 413 x -= pi; 414 sign = !sign; 415 } 416 417 if (x > piover2) 418 x = pi - x; 419 420 if (x < piover4) { 421 y = x * fouroverpi; 422 z = y * y; 423 r = y * (((((((-0.202253129293E-13 * z + 0.69481520350522E-11) * z - 424 0.17572474176170806E-8) * z + 0.313361688917325348E-6) * z - 425 0.365762041821464001E-4) * z + 0.249039457019271628E-2) * z - 426 0.0807455121882807815) * z + 0.785398163397448310); 427 } else { 428 y = (piover2 - x) * fouroverpi; 429 z = y * y; 430 r = ((((((-0.38577620372E-12 * z + 0.11500497024263E-9) * z - 431 0.2461136382637005E-7) * z + 0.359086044588581953E-5) * z - 432 0.325991886926687550E-3) * z + 0.0158543442438154109) * z - 433 0.308425137534042452) * z + 1.0; 434 } 435 return sign ? -r : r; 436 } 437 438 /* cos(x) Return cosine, x in radians, by identity */ 439 440 static double cos(x) 441 double x; 442 { 443 x = (x < 0.0) ? -x : x; 444 if (x > twopi) /* Do range reduction here to limit */ 445 x = x - (aint(x / twopi) * twopi); /* roundoff on add of PI/2 */ 446 return sin(x + piover2); 447 } 448 449 /* tan(x) Return tangent, x in radians, by identity */ 450 451 static double tan(x) 452 double x; 453 { 454 return sin(x) / cos(x); 455 } 456 457 /* sqrt(x) Return square root. Initial guess, then Newton- 458 Raphson refinement */ 459 460 double sqrt(x) 461 double x; 462 { 463 double c, cl, y; 464 int n; 465 466 if (x == 0.0) 467 return 0.0; 468 469 if (x < 0.0) { 470 fprintf(stderr, 471 "\nGood work! You tried to take the square root of %g", 472 x); 473 fprintf(stderr, 474 "\nunfortunately, that is too complex for me to handle.\n"); 475 exit(1); 476 } 477 478 y = (0.154116 + 1.893872 * x) / (1.0 + 1.047988 * x); 479 480 c = (y - x / y) / 2.0; 481 cl = 0.0; 482 for (n = 50; c != cl && n--;) { 483 y = y - c; 484 cl = c; 485 c = (y - x / y) / 2.0; 486 } 487 return y; 488 } 489 490 /* atan(x) Return arctangent in radians, 491 range -pi/2 to pi/2 */ 492 493 static double atan(x) 494 double x; 495 { 496 int sign, l, y; 497 double a, b, z; 498 499 x = (((sign = (x < 0.0)) != 0) ? -x : x); 500 l = 0; 501 502 if (x >= 4.0) { 503 l = -1; 504 x = 1.0 / x; 505 y = 0; 506 goto atl; 507 } else { 508 if (x < 0.25) { 509 y = 0; 510 goto atl; 511 } 512 } 513 514 y = aint(x / 0.5); 515 z = y * 0.5; 516 x = (x - z) / (x * z + 1); 517 518 atl: 519 z = x * x; 520 b = ((((893025.0 * z + 49116375.0) * z + 425675250.0) * z + 521 1277025750.0) * z + 1550674125.0) * z + 654729075.0; 522 a = (((13852575.0 * z + 216602100.0) * z + 891080190.0) * z + 523 1332431100.0) * z + 654729075.0; 524 a = (a / b) * x + atanc[y]; 525 if (l) 526 a=piover2 - a; 527 return sign ? -a : a; 528 } 529 530 /* atan2(y,x) Return arctangent in radians of y/x, 531 range -pi to pi */ 532 533 static double atan2(y, x) 534 double y, x; 535 { 536 double temp; 537 538 if (x == 0.0) { 539 if (y == 0.0) /* Special case: atan2(0,0) = 0 */ 540 return 0.0; 541 else if (y > 0) 542 return piover2; 543 else 544 return -piover2; 545 } 546 temp = atan(y / x); 547 if (x < 0.0) { 548 if (y >= 0.0) 549 temp += pic; 550 else 551 temp -= pic; 552 } 553 return temp; 554 } 555 556 /* asin(x) Return arcsine in radians of x */ 557 558 static double asin(x) 559 double x; 560 { 561 if (fabs(x)>1.0) { 562 fprintf(stderr, 563 "\nInverse trig functions lose much of their gloss when"); 564 fprintf(stderr, 565 "\ntheir arguments are greater than 1, such as the"); 566 fprintf(stderr, 567 "\nvalue %g you passed.\n", x); 568 exit(1); 569 } 570 return atan2(x, sqrt(1 - x * x)); 571 } 572 #endif 573 574 /* Calculate passage through surface 575 576 If the variable PARAXIAL is true, the trace through the 577 surface will be done using the paraxial approximations. 578 Otherwise, the normal trigonometric trace will be done. 579 580 This routine takes the following inputs: 581 582 RADIUS_OF_CURVATURE Radius of curvature of surface 583 being crossed. If 0, surface is 584 plane. 585 586 OBJECT_DISTANCE Distance of object focus from 587 lens vertex. If 0, incoming 588 rays are parallel and 589 the following must be specified: 590 591 RAY_HEIGHT Height of ray from axis. Only 592 relevant if OBJECT.DISTANCE == 0 593 594 AXIS_SLOPE_ANGLE Angle incoming ray makes with axis 595 at intercept 596 597 FROM_INDEX Refractive index of medium being left 598 599 TO_INDEX Refractive index of medium being 600 entered. 601 602 The outputs are the following variables: 603 604 OBJECT_DISTANCE Distance from vertex to object focus 605 after refraction. 606 607 AXIS_SLOPE_ANGLE Angle incoming ray makes with axis 608 at intercept after refraction. 609 610 */ 611 612 static void transit_surface() { 613 double iang, /* Incidence angle */ 614 rang, /* Refraction angle */ 615 iang_sin, /* Incidence angle sin */ 616 rang_sin, /* Refraction angle sin */ 617 old_axis_slope_angle, sagitta; 618 619 if (paraxial) { 620 if (radius_of_curvature != 0.0) { 621 if (object_distance == 0.0) { 622 axis_slope_angle = 0.0; 623 iang_sin = ray_height / radius_of_curvature; 624 } else 625 iang_sin = ((object_distance - 626 radius_of_curvature) / radius_of_curvature) * 627 axis_slope_angle; 628 629 rang_sin = (from_index / to_index) * 630 iang_sin; 631 old_axis_slope_angle = axis_slope_angle; 632 axis_slope_angle = axis_slope_angle + 633 iang_sin - rang_sin; 634 if (object_distance != 0.0) 635 ray_height = object_distance * old_axis_slope_angle; 636 object_distance = ray_height / axis_slope_angle; 637 return; 638 } 639 object_distance = object_distance * (to_index / from_index); 640 axis_slope_angle = axis_slope_angle * (from_index / to_index); 641 return; 642 } 643 644 if (radius_of_curvature != 0.0) { 645 if (object_distance == 0.0) { 646 axis_slope_angle = 0.0; 647 iang_sin = ray_height / radius_of_curvature; 648 } else { 649 iang_sin = ((object_distance - 650 radius_of_curvature) / radius_of_curvature) * 651 sin(axis_slope_angle); 652 } 653 iang = asin(iang_sin); 654 rang_sin = (from_index / to_index) * 655 iang_sin; 656 old_axis_slope_angle = axis_slope_angle; 657 axis_slope_angle = axis_slope_angle + 658 iang - asin(rang_sin); 659 sagitta = sin((old_axis_slope_angle + iang) / 2.0); 660 sagitta = 2.0 * radius_of_curvature*sagitta*sagitta; 661 object_distance = ((radius_of_curvature * sin( 662 old_axis_slope_angle + iang)) * 663 cot(axis_slope_angle)) + sagitta; 664 return; 665 } 666 667 rang = -asin((from_index / to_index) * 668 sin(axis_slope_angle)); 669 object_distance = object_distance * ((to_index * 670 cos(-rang)) / (from_index * 671 cos(axis_slope_angle))); 672 axis_slope_angle = -rang; 673 } 674 675 /* Perform ray trace in specific spectral line */ 676 677 static void trace_line(line, ray_h) 678 int line; 679 double ray_h; 680 { 681 int i; 682 683 object_distance = 0.0; 684 ray_height = ray_h; 685 from_index = 1.0; 686 687 for (i = 1; i <= current_surfaces; i++) { 688 radius_of_curvature = s[i][1]; 689 to_index = s[i][2]; 690 if (to_index > 1.0) 691 to_index = to_index + ((spectral_line[4] - 692 spectral_line[line]) / 693 (spectral_line[3] - spectral_line[6])) * ((s[i][2] - 1.0) / 694 s[i][3]); 695 transit_surface(); 696 from_index = to_index; 697 if (i < current_surfaces) 698 object_distance = object_distance - s[i][4]; 699 } 700 } 701 702 /* Initialise when called the first time */ 703 704 int main(argc, argv) 705 int argc; 706 char *argv[]; 707 { 708 int i, j, k, errors; 709 double od_fline, od_cline; 710 #ifdef ACCURACY 711 long passes; 712 #endif 713 714 spectral_line[1] = 7621.0; /* A */ 715 spectral_line[2] = 6869.955; /* B */ 716 spectral_line[3] = 6562.816; /* C */ 717 spectral_line[4] = 5895.944; /* D */ 718 spectral_line[5] = 5269.557; /* E */ 719 spectral_line[6] = 4861.344; /* F */ 720 spectral_line[7] = 4340.477; /* G'*/ 721 spectral_line[8] = 3968.494; /* H */ 722 723 /* Process the number of iterations argument, if one is supplied. */ 724 725 if (argc > 1) { 726 niter = atoi(argv[1]); 727 if (*argv[1] == '-' || niter < 1) { 728 printf("This is John Walker's floating point accuracy and\n"); 729 printf("performance benchmark program. You call it with\n"); 730 printf("\nfbench <itercount>\n\n"); 731 printf("where <itercount> is the number of iterations\n"); 732 printf("to be executed. Archival timings should be made\n"); 733 printf("with the iteration count set so that roughly five\n"); 734 printf("minutes of execution is timed.\n"); 735 exit(0); 736 } 737 } 738 739 /* Load test case into working array */ 740 741 clear_aperture = 4.0; 742 current_surfaces = 4; 743 for (i = 0; i < current_surfaces; i++) 744 for (j = 0; j < 4; j++) 745 s[i + 1][j + 1] = testcase[i][j]; 746 747 #ifdef ACCURACY 748 printf("Beginning execution of floating point accuracy test...\n"); 749 passes = 0; 750 #else 751 printf("Ready to begin John Walker's floating point accuracy\n"); 752 printf("and performance benchmark. %d iterations will be made.\n\n", 753 niter); 754 755 printf("\nMeasured run time in seconds should be divided by %.f\n", niter / 1000.0); 756 printf("to normalise for reporting results. For archival results,\n"); 757 printf("adjust iteration count so the benchmark runs about five minutes.\n\n"); 758 759 //printf("Press return to begin benchmark:"); 760 //gets(tbfr); 761 #endif 762 763 /* Perform ray trace the specified number of times. */ 764 765 #ifdef ACCURACY 766 while (TRUE) { 767 passes++; 768 if ((passes % 100L) == 0) { 769 printf("Pass %ld.\n", passes); 770 } 771 #else 772 for (itercount = 0; itercount < niter; itercount++) { 773 #endif 774 775 for (paraxial = 0; paraxial <= 1; paraxial++) { 776 777 /* Do main trace in D light */ 778 779 trace_line(4, clear_aperture / 2.0); 780 od_sa[paraxial][0] = object_distance; 781 od_sa[paraxial][1] = axis_slope_angle; 782 } 783 paraxial = FALSE; 784 785 /* Trace marginal ray in C */ 786 787 trace_line(3, clear_aperture / 2.0); 788 od_cline = object_distance; 789 790 /* Trace marginal ray in F */ 791 792 trace_line(6, clear_aperture / 2.0); 793 od_fline = object_distance; 794 795 aberr_lspher = od_sa[1][0] - od_sa[0][0]; 796 aberr_osc = 1.0 - (od_sa[1][0] * od_sa[1][1]) / 797 (sin(od_sa[0][1]) * od_sa[0][0]); 798 aberr_lchrom = od_fline - od_cline; 799 max_lspher = sin(od_sa[0][1]); 800 801 /* D light */ 802 803 max_lspher = 0.0000926 / (max_lspher * max_lspher); 804 max_osc = 0.0025; 805 max_lchrom = max_lspher; 806 #ifndef ACCURACY 807 } 808 809 //printf("Stop the timer:\007"); 810 //gets(tbfr); 811 #endif 812 813 /* Now evaluate the accuracy of the results from the last ray trace */ 814 815 sprintf(outarr[0], "%15s %21.11f %14.11f", 816 "Marginal ray", od_sa[0][0], od_sa[0][1]); 817 sprintf(outarr[1], "%15s %21.11f %14.11f", 818 "Paraxial ray", od_sa[1][0], od_sa[1][1]); 819 sprintf(outarr[2], 820 "Longitudinal spherical aberration: %16.11f", 821 aberr_lspher); 822 sprintf(outarr[3], 823 " (Maximum permissible): %16.11f", 824 max_lspher); 825 sprintf(outarr[4], 826 "Offense against sine condition (coma): %16.11f", 827 aberr_osc); 828 sprintf(outarr[5], 829 " (Maximum permissible): %16.11f", 830 max_osc); 831 sprintf(outarr[6], 832 "Axial chromatic aberration: %16.11f", 833 aberr_lchrom); 834 sprintf(outarr[7], 835 " (Maximum permissible): %16.11f", 836 max_lchrom); 837 838 /* Now compare the edited results with the master values from 839 reference executions of this program. */ 840 841 errors = 0; 842 for (i = 0; i < 8; i++) { 843 if (strcmp(outarr[i], refarr[i]) != 0) { 844 #ifdef ACCURACY 845 printf("\nError in pass %ld for results on line %d...\n", 846 passes, i + 1); 847 #else 848 printf("\nError in results on line %d...\n", i + 1); 849 #endif 850 printf("Expected: \"%s\"\n", refarr[i]); 851 printf("Received: \"%s\"\n", outarr[i]); 852 printf("(Errors) "); 853 k = strlen(refarr[i]); 854 for (j = 0; j < k; j++) { 855 printf("%c", refarr[i][j] == outarr[i][j] ? ' ' : '^'); 856 if (refarr[i][j] != outarr[i][j]) 857 errors++; 858 } 859 printf("\n"); 860 } 861 } 862 #ifdef ACCURACY 863 } 864 #else 865 if (errors > 0) { 866 printf("\n%d error%s in results. This is VERY SERIOUS.\n", 867 errors, errors > 1 ? "s" : ""); 868 } else 869 printf("\nNo errors in results.\n"); 870 #endif 871 return 0; 872 } 873