« PreviousContinue »
and so we require that csin(x) equal some value taken
as follows. According to the rule for accuracy of numbers, the internal representation of the argument must lie within [x e/2, X + e/2). Now suppose that the internal representation is near an end point of the legal interval, and that the granularity of the machine (i.e., the difference between adjacent internal numeric representations) in that region of the real number line is near e (which would be the coarsest allowed, given
accuracy of d digits). Given this worst case, we
would still want a value returned for which the actual argument was closer to that internal representation than to immediately adjacent representations. This means that we allow for a variation of e/2 when the argument is converted from source to internal form, and another variation of e/2 around the internal representation itself. The maximum allowable variation along the x-axis is then simply the sum
of the worst-case variations : e/2 + e/2 = e. This is reasonable if we think of
a given internal form as representing not only a point on the real number line, but the set of points for which there is no closer internal form. all we know is that the source argument is somewhere within that set and all we require is that the computed value of the function be true for some (probably different) argument within the set. For accuracy d, the maximum width of the set is of course e.
It should be noted that the first allowed variation of e / 2 is inherent in the process of decimal (source) to, e.g., , binary (internal) conversion. The case for allowing a variation of e / 2 around the internal representation itself is somewhat weaker. one insists on exact results within the internal numerical manipulation, then the function would be allowed to vary only within the domain [x - e/2, X + e/2), but we did not require this in the tests.
Note that the above scheme not only allows for the discrete nature of the machine, but also for numeric instability in the function itself. Mathematically, if the value of an argument
is known to six places, it does not follow that the value of the function is known to six places; the error may
be considerably more or less. For example, a function is often very stable near where its graph crosses the y-axis, but not the x-axis (e.8. cos (1 E-22)) and
very unstable where it crosses the X-axis but not the y-axis (e.g. SIN (21.99)). By allowing the cf(x) to take on any value in the specified domain, we impose strict accuracy where it can be achieved, and permit low accuracy
where appropriate. Thus, the pass/fail criterion is independent of both the argument and function; it reflects only how well the implementation computed, relative to a worst-case six-digit machine.
Finally, we must recognize that even if the value of a function is computable to high accuracy (as with cos (1E-22)), the graininess of the machine will again limit how accurately the result itself can be represented. For this reason, there is an additional allowance of e/2 around the result. This implies that even if the result is computable to, say, 20 digits, we never require more than 6 digits of accuracy.
Now all the preceding comments generalize quite naturally to functions of
many variables. We can then be guided in our treatment of the arithmetic operations by the above remarks on functions, if we recall that the operations may be thought of as functions of two variables, namely their operands. If we think of, say, subtraction as such a function (i.e. subtract (x,y) x-y), then the same considerations of argument accuracy
and mathematical stability pertain. Thus, we allow both operands to vary within their intervals, and simply require the result of the operation to
be within the extreme values so generated. Note that such a technique would be necessary for any of the usual functions which take two variables, such as some versions of arctan.
It should be stressed that the resulting accuracy tests represent only a very minimal requirement. The design goal was to permit even the grainiest machine allowed by the standard
to pass the tests;
conforming implementations, then, are inherently capable of passing. Many users will wish to impose more stringent criteria. For example, those interested in high accuracy, or implementors whose machines carry more than six digits, should examine closely the computed value and true value to see if the accuracy is what they expect.
The ANSI standard provides a loop capability, along with an associated control-variable, through the use of the FOR statement. The semantic requirements for this construction are particularly well-defined. Specifically, the effect of the FOR is described in terms of more primitive language features (IF, GOTO, LET, and REM), which are themselves not very vulnerable to misinterpretation. The tests accordingly are quite specific and extensive in the behavior they require. The standard tests are completely self-checking, since conformance depends only on the value
of the control-variable and number of times through the loop. The general design plan was not only to determine passing or failing, but also to display information allowing the user to examine the progress of execution. This should help you diagnose any problems. Note especially the requirement that the control variable, upon exit from the loop, should have the first unused, not the last used, value.
The FOR statement has no associated exceptions, but it does have
rich variety of errors, many of them context sensitive, and therefore somewhat harder for an implementation to detect. As always, if any error programs are accepted, the documentation must specify what meaning the implementation assigns to them.
The standard provides for storing numeric values in one- or two-dimensiona arrays. The tests for standard capabilities are all self-checking and quite straightforward
exercising some feature defined in the standard. Note the requirement that subscript values be rounded to integers; the program testing this must not cause an exception or the processor fails.
The exception tests ensure that the subscript out of range condition is handled properly. Note that it is here that the real semantic meaning of OPTION and DIM are exercised; they have little effect other than to cause or prevent the subscript exception for certain subscript values. Since this is a fatal exception,
you must check (since the program cannot) that the programs terminate at the right time, as indicated in their messages.
As with the FOR statement, there are a considerable number of syntactic restrictions. The thrust of these restrictions is to assure that OPTION precedes DIM, that DIM precedes references to the arrays that it governs, and that declared subscript bounds are compatible.
Three of the error programs call for INPUT from the user. This is to help you diagnose the actual behavior of the implementation if it accepts the programs. The first of these,
lets you try to reference an array with a subscript of oor 1 when OPTION BASE 1 and DIM A(0) have been specified, to See when an exception occurs.
The second, #81, allows you to try a subscript of 0 or 1 for
1 an array whose DIM statement precedes the OPTION statement.
The third program using INPUT, #84, is a bit more complex and has to
do with double dimensioning. If there are two DIM statements for the same array, the implementation has a choice of several plausible interpretations. We have noted five such possibilities and have attempted to distinguish which,
if any, seems to apply. Since the only semantic effect of DIM is to cause or prevent an exception for a given array reference, however, it is necessary to run the program three times to see when exceptions occur and when they don't, assuming the processor hasn't simply rejected the program outright. Your input-reply simply tells the program which of the three executions it is currently performing. For each execution, you must note whether an exception occurred or not and then match the results against the table in the program. Suppose, for instance, that you get an exception the first time but not the second or third. That would be incompatible with all five interpretations except number 4, which is that the first DIM statement executed sets the size
of the array and it is never changed thereafter. As usual, check the documentation to make sure it correctly describes what happens.
This group fully exploits the properties of some
the control facilities which were tested in a simpler way in group 4. As before, there seemed no good way to provide diagnostics for failure of standard tests, since the behavior of a failing processor is impossible to predict. Passing implementations will cause the 11***
TEST PASSED ***" message to appear, but certain kinds of failures might cause the programs to abort, without producing a failure message. Check Volume 2 for an example of correct output.
Most of the tests in this group are self-explanatory, but the one checking address stacking deserves some comment. The standard describes the effect of issuing GOSUBS and RETURNS in terms of a stack of return addresses, for which the GOSUB adds a new address to the top, and the RETURN uses
the most recently added address. Thus, we get a kind of primitive recursion in the control structure (although without any stacking of data). Note that this description allows complete freedom in the placement of GOSUBS and RETURNS in the source code. There is no static association of any RETURN with any GOSUB. The test which verifies this specification computes binomial coefficients, using the usual recursive formula. The logic of the program is a bit convoluted, but intentionally
exercise the stacking mechanism vigorously.
The ON-GOTO tests are all readily understandable. The one thing you might want to watch for is that the processor rounds the expression controlling the ON-GOTO to the nearest integer, as specified in the standard. Thus, "ON .6 GOTO", "ON 1 GOTO", and "ON 1.4 GOTO" should all have the same effect; there should be no out of range exception for values between 5 and 1.
This group tests the facilities for establishing a stream of data in the program and accessing it sequentially. This feature has some subtle requirements, and it would be wise to read the standard especially carefully so that you understand the purpose of the tests.
All but the last of these tests are reasonably simple. The last test dealing with the general properties of READ and DATA, although self-checking, has somewhat complex internal logic. It assures that the range of operands of READ and DATA can overlap freely and that a given datum can be read as numeric at one time and as a string at a later time. If you need to examine the internal logic closely, be sure to use the REM statements at the beginning which break down the structure of the READ and DATA lists for you.
The exceptions can be understood directly from the programs. Note that string overflow may or may not occur, depending on the implementation-defined maximum string length. If overflow (loss of data) does occur, the processor must report an exception and execution must terminate. If there is no exception report, look carefully at the output to assure that no loss of data has occurred.
All of the error tests display results if the implementation accepts them, allowing you
to check that the documentation matches the actual behavior of the processor. Some of the illegal constructs are likely candidates for enhancements and thus the diagnostic feature is important here.