by Robin Vowels
Recent discussion about the decimal division drew attention to
Extra care is needed when using fixed-point arithmetic including
FIXED DECMAL and FIXED BINARY.
In general, PL/I attempts to accommodate all digits of the result,
whether the operation be addition, subtraction, multiplication,
Consider the addition of A and B, where A and B are defined in
DECLARE (A, B) FIXED (4,2);
The precision of A + B is (5,2), which accommodates all
possible results without overflowing, including the addition of
such values as 99.99 and 99.99, which gives 199.98
Multiplication of A and B produces a result of precision
(9,4) [A and B are assumed to be defined as above]
which acommodates the product of the smallest values 0.01 and 0.01,
It also accommodates the product A*B of the largest possible values
99.99 and 99.99, giving 9998.0001
Division is another kettle of fish.
Division always produces a result having the maximum number of
digits permitted by the installation.
The number of digits after the decimal point depends on the precisions
of the operands.
Bear in mind that PL/I will attempt to produce the greatest number of
digits after the decimal point, consistent with the aim of producing
an accurate and sensible result when the largest number is divided by
For the result, the number of digits before the point depends on the
number of digits before the point in the dividend.
The result can overflow when the largest value is divided
by a value smaller than unity.
Consider DECLARE (A, B) FIXED DECIMAL (5, 2);
The precision of A/B is (15,10).
If, however, the declarations are changed to:
DECLARE (A, B) FIXED DECIMAL (15, 8);
Then the precision of A/B is (15, 0) -- that is, an integer.
There are no fraction digits.
This outcome is governed by the maximum number of digits
permitted by the installation, namely 15.
If the maximum number of digits permitted is raised to 31,
an entirely different precision is obtained.
A/B then yields a result of precision (31, 16), which is a
consistent with that obtained above when A and B are defined
with precision (5,2).
This "phenomenon" occurs when the number of digits in the dividend
exceeds half the maximum permitted.
No doubt the maximum number of digits (15) in the early S/360
implementations was influenced by hardware considerations.
However, with the VisualAge PL/I compiler it is possible to specify
the maximum number of digits as 31. The Kednos compiler (Compaq)
already has this limit.
The VisualAge PL/I compiler option is LIMITS (FIXEDDEC(31)), specified
on the PROCESS statement thus:
%PROCESS LIMITS (FIXEDDEC(31));
Just for the record, the precision of the result of division is
where q = M - p1 + q1 - q2.
M = maximum number of digits permitted;
The precision of the dividend is (p1, q1),
The precision of the divisor is (p2, q2).
If decimal division is required in a simple statement,
D = A/B may be adequate, provided that p1 < M/2.
If the division is only part of the operations of an expression,
it would be better to use the DIVIDE built-in function to ensure
that fraction digits are not lost.
In any case, when execution time is important, DIVIDE should be used
to avoid generating unnecessary result digits.
DIVIDE can also be used to accommodate the division of the
largest possible value of dividend by the smallest possible value
Similar considerations apply when the operands are FIXED BINARY,
when scaled results are permitted.
When division is used in an expression, it would be advisable
to use the DIVIDE built-in function to produce a result having
a precision consistent with the other components in the expression.
In particular, specify the number of digits after the decimal/binary
point desired by the user.
The (new) Built-in Functions of IBM VisualAge PL/I
by Robin Vowels
In issue No. 3 of The PL/I Newsletter, we looked at some of the
(new) built-in functions of IBM VisualAge PL/I.
Here we discuss some more of them.
ALLOCATE(n) allocates storage of size n and returns
a pointer to that storage.
AVAILABLEAREA(x) returns the size of the largest contiguous
area of memory that would be available for allocation from the
BITLOCATION(x) returns the location (in the range 0 to 7)
of x within the byte where x is stored. (x is
CURRENTSIZE. CURRENTSIZE(T) returns the number of bytes
in the variable or structure T.
EDIT(Value, picture_pattern) returns the value in the
form specified by the picture pattern.
e.g., EDIT (12.345, '9999.999') returns '0012.345'
ENDFILE(filename) returns '1'B if the file is
positioned at the end, or '0'B otherwise.
If the file is not open, the ERROR condition is raised.
EPSILON(X) returns a floating-point value that is the
difference between x and the next representable positive value
when x is 1.0
EXPONENT(X) returns the exponent E of the floating-point
value X as an integer. E is specified by the relation
r**(E-1) <= |X| <= r**E, where r is the radix of X.
FILEOPEN(filename) returns '1'B if the file
filename is open, and '0'B otherwise.
GAMMA(X) returns an approximation to the Gamma function
HANDLE. HANDLE(T) returns a handle to the typed
HEX Returns the hexadecimal form of the character string
argument. HEX('abc') returns '616263' on an ASCII machine,
and '818283' on an EBCDIC machine.
HEX(S, ' ') is the same as HEX(S), except that blanks are
inserted in the returned hexadecimal string - after every eighth
HEXIMAGE is like HEX. HEXIMAGE(S, n) returns n
characters at the address S (as a hexadecial string).
HEXIMAGE(S, n, c) is the same as HEXIMAGE(S, n) except that
the character c is inserted after every eighth character
of the hexadecimal string.
TALLY: Need to count the number of times that a given
appears in a sentence? or the number of times a word appears,
or any given group of characters? Then TALLY is for you!
TALLY(S, T) counts the number of times that string T appears
in string S. S and T can be CHARACTER, BIT, or GRAPHIC.
Usefulle Webbe Lynx ...
Peter Flass is building a resource at:
Peter Flass's PL/I home page.
The spring 2000 edition of the
COBOL and PL/I newsletter
has some topics about PL/I. The specific PL/I
topics may be viewed separately, or the entire newsletter in PDF
format may be downloaded.
Of particular interest is the use of PL/I as a programming
language for the Common Gateway Interface (CGI).
The PL/I Connection Newsletter No. 11 , December 1997.
The PL/I Connection Newsletter No. 10 , April 1997.
Three Workshops will be offered in Northeast Brasil at CESV without payment
for the Education for Professional Programming Education with assistance
Centro de Educacao de Säo Vicente/RN
Rua Boa Sorte 2001
59340-000 Säo Vicente/RN
Organisation Sra Professora Neci Olieira - Phone: 0055-84-4360210
VA PLI - Date: Feb and Sep 2002 (3 Days)
Pagamento:Gratuido Liguagem: Portugues/Engles
Contens: Practical Programming in IBM VisualAge PL/I - Workshop for
OS/390 V2R10 and z/OS
VA COBOL - Date: Feb and Sep 2002 (3 Days)
Pagamento: Gratuido Liguagem: Portugues/Engles
Contens: Practical Programming in IBM VisualAge COBOL-Workshop for
OS390 V2R10 and z/OS
HLASM - Date: Feb and Sep 2002
Pagamento: Gratuido Liguagem: Portugues/Engles
Contens: Practical Programming in IBM HLASM V1R4-Workshop for
OS/390 V2R10 and z/OS
For more Information about Sertäo -> www.prefeituradecurraisnovos.com.br
Presidente CESV Wilhelm Kopschek
Date sent: Wed, 04 Jul 2001 11:42:37 +0200
To: firstname.lastname@example.org, email@example.com,
Current IBM PL/I offerings in the U.S.
IBM has enhanced VisualAge® PL/I with powerful features to increase
development productivity, simplify the maintenance of your legacy code,
and provide seamless portability from your host to your workstation. It
also provides the tools needed to identify your Year 2000 date-related
fields on both OS/2 and Windows NT.
VisualAge PL/I Enterprise Edition Version 2.1 combines the two separate
offerings from the previous release of VisualAge PL/I (Standard and
Professional) into a single offering available on both the OS/2 and
Windows NT platforms. By doing so, it provides these productivity
- remote edit/compile
- redevelopment tools
- cross platform development
- year 2000 assessment strategies, find and fix, and test capabilities
Also included as an extra bonus offering is VisualAge CICS® Enterprise
Application Development, which enables CICS host application development
on the workstation.
- Product number 04L6564: VisualAge PL/1 Enterprise V2.1
- 04L6565: VisualAge PL/I Enterprise V2.1 Upgrade from Prior
IBM PL/I Workstation or Competitive Products Program Package
- 04L6567: VisualAge PL/I Enterprise V2.1 Upgrade from
Professional V2.0 Program Package CD-ROM
- 04L6566: VisualAge PL/I Enterprise V2.1 Upgrade from Standard
V2.0 Package CD-ROM
From: Tom Van Vleck , Multicians
Date: Tue, 23 Apr 2002 13:35:42 GMT
An HTML version of "The Multics PL/1 Compiler"
by Bob Freiburghouse from the 1969 FJCC has
been added to the Multics website at
with Bob's permission.
This paper describes the Multics version 1 PL/I
by Robin Vowels
Built-in functions are generic, and typically operate on any data type and
for an array of any number of dimensions (up to the maximum permitted
by the compiler).
DECLARE S GENERIC_ALL
(S1 WHEN FIXED BINARY,
S2 WHEN FIXED DECIMAL,
S3 WHEN FLOAT,
S4 WHEN FLOAT (16));
If a user attempts to write a generic function, it is necessary to
provide the procedures to perform the desired operation on each of the
data types that he wants.
As well, if the argument(s) can be arrays, the user must also provide
procedures to perform the desired operation on arrays of any number
of dimensions. Each procedure dealing with an array must also be
duplicated to deal with each of the different types that the user wants.
Thus, if there are four data types (integer, decimal, float, float (16)),
and the compiler supports arrays having 10 dimensions, then 40 procedures
must be provided.
It would be better to have a new attribute GENERIC_ALL
which specifies that any procedure
in the family is assumed to apply to scalars and arrays of any
Such a facility would require the user to write just four procedures
(instead of forty in the above example).
by Robin Vowels
To improve debugging, incorporate the following in your procedures.
DECLARE (PROCNAME, SOURCELINE) BUILTIN;
CALL S (PROCNAME, SOURCELINE, A, B);
If your subroutine writes out an error message, this will enable you
to include both the line number and name of the procedure that called
the current one, thus:
S: PROCEDURE (Caller, Line, E, F)
DECLARE Caller CHARACTER (31) VARYING, Line FIXED BINARY (31);
PUT SKIP LIST ('... called from procedure ' || Caller ||
' at line ' || Line);
Also available is the built-in function SOURCEFILE that allows you
to print the name of the file from which any portion of code
is compiled. Handy when there is a number of INCLUDE statements
in the program.
Brain Teasers: Eberhard's PL/I Problem
by Eberhard Sturm
For an explanation of the solution of the previous problem I only have
to present the one given by Tony Shediak from Australia:
WHAT: PROC OPTIONS (MAIN);
DCL 1 A,
3 A FIXED BIN INIT (1),
3 B FIXED BIN INIT (2);
DCL B FIXED BIN INIT (3);
CALL WHAT (B, B, (B)); /* stand-alone B is the one */
PUT (B); /* 3 3 3 */
WHAT: PROC (X, Y, Z);
/* (B) generates a dummy arg, hence Z is a copy of B */
DCL (X, Y, Z) FIXED BIN;
DCL 1 A,
2 B FIXED BIN INIT (4),
2 A FIXED BIN INIT (5);
A = A + B; /* A.B = A.B + A.B = 4 + 4 = 8 */
/* A.A = A.A + A.B = 5 + 8 = 13*/
Z = X + A.A; /* Z = 3 + 13 = 16 */
X = Z + A.B; /* X = 16 + 8 = 24 */
Y = Y + A.A.A; /* Y = 24 + 1 = 25 (X & Y are one and the same) */
So the output is 25. Things to note:
Thank you, Tony, for your excellent explanation. My comment on this
- (B) generates a dummy argument hence Z is a COPY of B. When Z is
changed B is NOT changed
- Structure arithmetic in A = A + B (add B to every element of A).
- X and Y and B (stand-alone) are the same area of storage - the
argument B is passed by address.
- When resolving the reference to B in the call stmt - the compiler
always chooses the stand-alone variable, not the element of the
structure, as both are declared in the same block.
- The call What stmt is NOT recursive - it is OK to have a subroutine
what contained within and called from procedure what
- Last but not least - I hope I've got his right as I've been
teaching PL/I for 14 years.
- PL/I has pitfalls when you don't choose variable names carefully.
- Always use the compiler option "rules(nolaxqual"! Then you are not
allowed to specify only B (at least one error less).
- Don't pass the same variable to a procedure more than once. You
perhaps don't know what the procedure assigns to the respective
- Let the compiler help you protect "input" parameters being assigned
values by specifying the "nonassignable" attribute.
This month's problem
Whereas the previous problem presented a situation which you
hopefully never will experience, the new problem asks for a solution
you probably have wanted sometimes. In the following code fragment
you have to replace the ellipses by some statements in order to being
able to use array constants:
%ARRAY: procedure ... ;
procedure options (main);
call Sub (ARRAY((1, 2, 3)));
call Sub (ARRAY((17, 200, 3, 40, 150, 98)));
dcl ... /* X */;
dcl ... /* Y */;
As you may have guessed, a constant array of any dimension should be
passed to a subroutine which in turn wants to use it as an array Y.
ARRAY is not a builtin function but a preprocessor procedure you have
to provide, too. The extra pair of parentheses is necessary when
using the MACRO preprocessor. To keep the code small I allow you to
give the array Y the attribute "fixed bin (8) unsigned" :-)! In this
way you may give any number of elements to ARRAY, the value of each
between 0 and 255. If you've found the solution I've in mind, you can
start thinking about "fixed bin (15) signed"!
Please send your solutions to Eberhard Sturm at: sturm@UNI-MUENSTER.DE