Square Roots In DFSORT
MartinPacker 11000094DH Visits (5027)
DFSORT's Arithmetic operators can do many things but the one thing they can't do is take the square root of a number.
You might think that's minor but it means you can't calculate a Standard Deviation. (Variance is fine but dimensionally not so nice when used in conjunction with the Mean.) And I needed Standard Deviation in a real live customer situation.
So I set out to "roll my own". And here's how I did it...
There is a well known technique for calculating square roots - the Babylonian Method (or Heron's Method). It's not terribly advanced maths, frankly: I think I was taught it at about age 14. It goes like this:
As you can see it's a really very simple method and converges reasonably quickly. And it's not difficult to program, except for one thing: DFSORT doesn't have a looping capability. (Yes it loops over the input records, but not in the way I mean.)
Before I show you some code let me talk about some considerations:
A reasonable question to ask is whether integer square roots are useful...
A standard technique with DFSORT's Arithmetic operators is to boost the integer values to represent the number of decimal places required. So, for example, to add together 3.14 and 2.72 you'd be storing them as 314 and 272. Then you'd add these integers and use EDIT=(I.TT) to display them as "4.86". So if you wanted to take the square root of 3.1416 you'd multiply by 10,000 and then apply my technique, using EDIT=(IIII.TT) to move the decimal point back.
Structure Of The Example
This is a standard DFSORT invocation. Rather than give you the usual scaffolding I'll show you the main components:
You could, in fact, structure this program differently. It might (though I haven't worked through it) combine everything into a single INREC statement (apart from the COPY specification).
Some Sample Data
In my SORTIN I have the following lines:
100 5 1 27 3 4 999 999999 99999999
In other words a single field in each record in EBCDIC (rather than binary) form. (You can generalise all that follows with a small amount of thought.)
(In my test case this is instream data - so it's fixed-length records and hence no RDW).
Parsing EBCDIC Numbers.
In this simple case I created a set of records with binary representations of the numbers using INREC:
OPTION COPY INREC FIEL
Here the UFF does the parsing and it parses the 10 bytes starting in position 1, with the output being 4-byte BI fields.
A SYMNAMES Deck To Simplify Things
You may know you can define symbols with DFSORT. Here's the deck I used in this example:
//SYMNAMES DD * INPUT,1,4,BI SQRT,*,4,BI PREV,*,4,BI /*
(You'll want a SYMNOUT DD to go with it.)
Taking The Square Root
In my example all the work is done in a series of IFTHEN clauses in an OUTREC statement:
Here's the code:
OUTREC IFTHEN=(WHEN=INIT, OVER
This you'll recognise as an unrolled loop. The "..." says you can repeat the previous IFTHEN clause as many times as you like.
A good question would be "how many times?" I found good convergence after 15. For example, the 99999999 value went to 10028 in 15 iterations and 10000 in 16. You could pick 20 and be sure of getting an accurate value.
Let's talk about control flow through the queue of IFTHEN clauses...
In the past I've likened multiple IFTHEN clauses to a pipeline. This example shows why.
Printing The Results
The following is a quite complicated piece of report formatting. Its aim is to format numbers and then squeeze out the resulting leading blanks. At the same time it has to preserve blanks in text.
OUTFIL IFTHEN=(WHEN=INIT, BUIL
Let's examine it more closely:
Output Using The Sample Data
The output below isn't particularly pretty. I wanted to use the OUTFIL code I've shown you to demonstrate the ability to selectively squeeze blanks out.
x=100 sqrt(x)=10 sqrt(x)*sqrt(x)=100 prev est=10 x=5 sqrt(x)=2 sqrt(x)*sqrt(x)=4 prev est=2 x=1 sqrt(x)=1 sqrt(x)*sqrt(x)=1 prev est=1 x=27 sqrt(x)=5 sqrt(x)*sqrt(x)=25 prev est=5 x=3 sqrt(x)=2 sqrt(x)*sqrt(x)=4 prev est=1 x=4 sqrt(x)=2 sqrt(x)*sqrt(x)=4 prev est=2 x=999 sqrt(x)=31 sqrt(x)*sqrt(x)=961 prev est=31 x=999999 sqrt(x)=999 sqrt
Although this has been quite a complex post I hope it's taught you a few new DFSORT tricks. Actually, the "meat" of it is the Square Root calculation - which I'm using "in anger" (AKA "for real") right now. And the technique should be readily adaptable.
The question is: Are there other Newt