( Title: A utiility for testing %f%stack effects File: %f%tester.fs Author: David N. Williams License: Public Domain, John Hayes Version: 1.4.3, January 18, 2021 Generated: %DATE% from fptester.tmpl For any code derived from John Hayes' tester program: ) \ (C) 1995 JOHNS HOPKINS UNIVERSITY / APPLIED PHYSICS LABORATORY \ MAY BE DISTRIBUTED FREELY AS LONG AS THIS COPYRIGHT NOTICE REMAINS. ( This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. The rest is public domain, based on the ttester derivative by Anton Ertl and its xftester derivative by David N. Williams. Explanatory material was provided by C. G. Montgomery in March, 2009, with helpful comments from Krishna Myneni. ) \ *** HOW TO GENERATE A NEW FP TESTER ( This file is a template for generating two Hayes-style testers, for floating-point words that operate on one of the Forth-gmpfr stacks of cell-sized record pointers for gmp floats or mpfr reliable floats. It is to be translated by substituting replacements for text elements including a leading and trailing % as in %NAME%, for example, by using the search and replace function of a text editor, or a script based on the Forth 2012 words SUBSTITUTE and REPLACES. There are two essential substitutions, one for %F% and one for %f%, which are to be replaced by the upper and lower case versions, respectively, of a text token not containing white space. The date of generation can also be substituted for %DATE%, which appears only in the header above; and the test file for the tester should be substituted for %TESTER-TEST% in the USAGE section below. The code assumes that fetch, store, equality, stack depth and a few other words are defined for the separated stack, with names resulting from the substitutions in patterns such as %f%@, %f%!, %f%=, and %f%depth or %F%DEPTH. There is a little bit of code that is needed to initialize arrays used to store data from stacks that are consistent with our garbage-collected record stack scheme. Although this template is currently applied only to such stacks, that code is marked as REMOVABLE FOR STACKS WITH NO GC. This section, HOW TO GENERATE A NEW FP TESTER, should be removed from the generated file. In any case, if its text undergoes substitution, it will become incoherent. The copyright and license in the header above should be retained. ) \ *** USAGE ( PUBLIC INTERFACE WORDS tests: %F%{ %F%-> }%F% %F%T-ABS-TOLERANCE %F%T-REL-TOLERANCE %F%T-TEST=-XT %F%T-DATUM= %F%T-ABS= %F%T-REL= %F%T-REL0= SET-%F%T-MODE-EXACT SET-%F%T-MODE-ABS SET-%F%T-MODE-REL SET-%F%T-MODE-REL0 config: MAX-%F%T-INITIALS MAX-%F%T-RESULTS PUBLIC AUXILIARY WODS tests: %F%T-TEST=-XT %F%T-DATUM= %F%T-ABS= %F%T-REL= %F%T-REL0= errors: %F%T-ERROR-XT %F%T-ERROR-DEFAULT %F%T-ERROR-INDEX arrays: %F%T-INITIALS %F%T-RESULTS %F%T-GOALS %F%T-A@ %F%T-A! %F%T-A= The public interface words are all that are needed for normal testing, including using the built-in fp comparisons and changing their tolerances. The public auxiliary words are made available for users who want to add fp comparisons, or to implement more elaborate error reporting, such as counting errors, or dumping fp results and goals when something is wrong, or reporting a particular fp error with the help of %F%T-ERROR-INDEX. The file %f%tester-errors.fs does that, and is loaded by %TESTER-TEST%.fs. The basic test usage takes the form: ) \ ( %f%initials) %F%{ <%f%results> %F%-> <%f%goals> }%F% ( %f%initials) ( Angle brackets around a data designation for a particular stack indicate code that produces the data. Here %f%initials stands for possibly nonempty %f%stack contents at the start of the test. The sequence starting with %F%{ saves the %f%initials and removes them from the %f%stack, executes the code <%f%results> that produces the %f%stack %f%results, saves and removes the %f%results from the %f%stack, executes the <%f%goals> code that produces the %f%stack %f%goals, saves and removes them, compares the saved %f%results and %f%goals, reports when there is a discrepancy between the two, and restores the saved %f%initials. The words %F%{, %F%->, and }%F% ignore non-%f%stacks. Many examples can be found in the file %TESTER-TEST%. Floating-point testing can involve further complications. There are the perennial issues of floating-point value comparisons. Exact equality is specified by SET-%F%T-MODE-EXACT [the default]. If one of the included approximate equality tests is desired, execute SET-%F%T-MODE-ABS, SET-%F%T-MODE-REL, SET-%F%T-MODE-AREL, or SET-%F%T-MODE-REL0. Corresponding to these, the public fvariables %F%T-ABS-TOLERANCE, %F%T-REL-TOLERANCE, and %F%T-AREL-TOLERANCE contain the values to be used in comparisons by the words %F%T-ABS=, %F%T-REL=, and %F%T-AREL=. %F%T-REL0= conditionally executes both %F%T-ABS= and %F%T-REL=. Here is an example which uses %F%T-REL= : SET-%F%T-MODE-REL %f%" 1e-6" %F%T-REL-TOLERANCE %f%! %f%{ %f%" 1.414214" %f%-> %f%" 2" %f%-sqrt }%f% Note that comparisons in this mode are not symmetric. Measured values must be in the %f%results, and reference values in the %f%goals. The user may supply a different equality test by storing his own xt in the public variable %F%T-TEST=-XT. Storage of %f%initials, %f%results, and %f%goals includes overflow checking. The user can change the default storage allocation by defining one or both of the following CONSTANT's *before* loading this file: MAX-%F%T-INITIALS MAX-%F%T-RESULTS The default xt's and tolerances can be changed after loading this file. Loading %f%tester.fs does not change BASE. ) BASE @ DECIMAL \ *** ERRORS VARIABLE %F%T-ERROR-XT VARIABLE %F%T-ERROR-INDEX \ set to zero after unequal array size \ comparison, or [index+1] after deepest \ unequal element comparison at index : %F%T-ERROR-DEFAULT ( c-addr u -- ) ( Display an error message followed by the line that had the error. ) TYPE SOURCE TYPE CR ; ' %F%T-ERROR-DEFAULT %F%T-ERROR-XT ! : %F%T-ERROR %F%T-ERROR-XT @ EXECUTE ; \ vectored error reporting \ *** COMPARISONS VARIABLE %F%T-TEST=-XT : %F%T-TEST= (%f%: x y -- ) ( -- flag ) %F%T-TEST=-XT @ EXECUTE ; \ The sign of these tolerances must be plus. %f%" 0e0" %F%CONSTANT %F%T-ABS-TOLERANCE-DEFAULT %f%" 1e-12" %F%CONSTANT %F%T-REL-TOLERANCE-DEFAULT %F%VARIABLE %F%T-ABS-TOLERANCE %F%T-ABS-TOLERANCE-DEFAULT %F%T-ABS-TOLERANCE %f%! %F%VARIABLE %F%T-REL-TOLERANCE %F%T-REL-TOLERANCE-DEFAULT %F%T-REL-TOLERANCE %f%! : %F%T-DATUM= (%f%: x y -- ) ( -- flag ) ( Leave TRUE if the two %f%loats have equivalent internal representations. Else leave FALSE. ) %f%= ; \ |x - y| < eps : %F%T-ABS= (%f%: x y -- ) ( -- flag ) %f%- |%f%| %F%T-ABS-TOLERANCE %f%@ %f%< ; \ |m - r| < eps * |r| (note the asymmetry) : %F%T-REL= (%f%: meas ref -- ) ( -- flag ) %f%swap %f%-reldiff |%f%| %F%T-REL-TOLERANCE %f%@ %f%< ; : %F%T-REL0= (%f%: meas ref -- ) ( -- flag ) %f%dup %f%0= IF %F%T-ABS= ELSE %F%T-REL= THEN ; : SET-%F%T-MODE-EXACT ( -- ) ['] %F%T-DATUM= %F%T-TEST=-XT ! ; : SET-%F%T-MODE-ABS ( -- ) ['] %F%T-ABS= %F%T-TEST=-XT ! ; : SET-%F%T-MODE-REL ( -- ) ['] %F%T-REL= %F%T-TEST=-XT ! ; : SET-%F%T-MODE-REL0 ( -- ) ['] %F%T-REL0= %F%T-TEST=-XT ! ; SET-%F%T-MODE-EXACT \ *** ARRAYS ( The first array slot has index zero. A tick prefix in stack somments means "address". ) \ USER CONFIG DEFAULTS [UNDEFINED] MAX-%F%T-INITIALS [IF] 32 CONSTANT MAX-%F%T-INITIALS [THEN] [UNDEFINED] MAX-%F%T-RESULTS [IF] 32 CONSTANT MAX-%F%T-RESULTS [THEN] \ Array buffers begin with a cell-sized count. CREATE %F%T-INITIALS MAX-%F%T-INITIALS 1+ CELLS ALLOT CREATE %F%T-RESULTS MAX-%F%T-RESULTS 1+ CELLS ALLOT CREATE %F%T-GOALS MAX-%F%T-RESULTS 1+ CELLS ALLOT \ BEGIN: REMOVABLE FOR STACKS WITH NO GC : 0%F%T-A ( +n 'buf -- ) \ assume n<>0 ( Initialize the array buffer for garbage collection by clearing the count and n, %f%element addresses. ) swap 1+ cells over + swap DO 0 i ! 1 cells +LOOP ; MAX-%F%T-INITIALS %F%T-INITIALS 0%F%T-A MAX-%F%T-RESULTS %F%T-RESULTS 0%F%T-A MAX-%F%T-RESULTS %F%T-GOALS 0%F%T-A \ END: REMOVABLE FOR STACKS WITH NO GC \ Overflow checking to be done elsewhere. : %F%T-A! ( +n 'buf -- ) ( %f%: %f%_[n-1] ... %f%_0 -- ) 2dup ! cell+ swap ( 'elems n) dup IF cells over + swap DO i %f%! 1 cells +LOOP ELSE 2drop THEN ; : %F%T-A@ ( 'buf -- +n ) ( %f%: -- %f%_[n-1] ... %f%_0 ) dup @ dup >r ( 'buf n r: n) IF cell+ r@ 1- cells over + DO i %f%@ -1 cells +LOOP ELSE drop THEN r> ; [UNDEFINED] CELL- [IF] : CELL- ( addr -- addr-cell ) [ 1 CELLS ] LITERAL - ; [THEN] : %F%T-A= ( 'buf1 'buf2 -- 0 true | 0 false | index+1 false ) ( Note that when %F%T-TEST=-XT is set to %F%T-REL=, the comparison is not symmetric; so the elements of the measured and reference array buffers at 'buf1 and 'buf2, respectively, must be fetched to the %f%stack in proper order. In the stack comments below, "m" and "r" stand for "measured" and "reference", to keep track in case the comparison xt is %F%T-REL=. Returns: 0 true: equal size and elements 0 false: sizes unequal index+1 false: sizes equal, deepest unequal elements at index ) 2dup @ swap @ <> IF 2drop 0 false EXIT THEN \ unequal size dup @ 0= IF 2drop 0 true EXIT THEN \ equal size and empty 2>r 0 true 2r> ( 0 true 'buf1 'buf2) dup @ ( count) >r \ last index + 1 = count r@ cells + swap r@ cells + 1 r> ( 0 true 'r_last 'm_last 1 last+1) DO ( 0 true 'r_index 'm_index) \ i = index+1 2dup %f%@ %f%@ ( %f%: m_index r_index) %F%T-TEST= 0= IF \ unequal elements 2drop ( 0 true) 2drop i false 0 0 LEAVE THEN cell- swap cell- swap ( 'r_[index-1] 'm_[index-1]) -1 +LOOP ( [0 true | index+1 false] x1 x2) 2drop ; \ *** TESTS : %F%{ ( %f%: %f%initials -- ) ( Save the %f%initials for restoration by }%F%, leaving the %f%stack empty. Clear %f%results and %f%goals storage. ) %f%depth dup MAX-%F%T-INITIALS > ABORT" TOO MANY %F%INITIALS" %F%T-INITIALS %F%T-A! 0 %F%T-RESULTS ! 0 %F%T-GOALS ! ; : %F%-> ( %f%: %f%esults -- ) ( Save the %f%results, leaving the %f%stack empty. ) %f%depth dup MAX-%F%T-RESULTS > ABORT" TOO MANY %F%RESULTS" %F%T-RESULTS %F%T-A! ; : }%F% ( %f%: %f%goals -- %f%initials ) ( Save the %f%goals, compare them with the saved %f%results, and restore the %f%initials. If the number of %f%results is not the same as the number of %f%goals, store zero in %F%T-ERROR-INDEX and execute the %F%T-ERROR vector. Else, if the numbers are the same but there is a deepest comparison failure at the index-th element, store index+1 in %F%T-ERROR-INDEX and execute the %F%T-ERROR vector. ) %f%depth dup MAX-%F%T-RESULTS > ABORT" TOO MANY %F%GOALS" %F%T-GOALS %F%T-A! %F%T-RESULTS %F%T-GOALS %F%T-A= ( 0 true | 0 false | index+1 false) IF ( 0) drop ELSE dup %F%T-ERROR-INDEX ! IF S" INCORRECT %F%RESULT: " ELSE S" WRONG NUMBER OF %F%RESULTS: " THEN %F%T-ERROR THEN %F%T-INITIALS %F%T-A@ ( len) drop ; BASE ! \ *** END of %f%tester.fs