I am having some trouble with some cobol code. In the following code bellow it causes these two errors. The compiler does not like when I try and do the addition.
CH7PPB.CBL:158: Error: 'NEW-DUES' is not numeric name
CH7PPB.CBL:161: Error: 'NEW-INSURANCE' is not numeric name
MOVE UNION-DUES TO OLD-DUES
MULTIPLY UNION-DUES BY .04 GIVING NEW-DUES
ADD UNION-DUES TO NEW-DUES
MOVE INSURANCE TO OLD-INSURANCE
MULTIPLY INSURANCE BY .03 GIVING NEW-INSURANCE
ADD INSURANCE TO NEW-INSURANCE
NEW-DUES and NEW-INSURANCE are defined as follows.
05 NEW-DUES PIC Z9(4).99.
05 NEW-INSURANCE PIC Z9(4).99.
Thank you for any help.
I'm glad you already solved the issue.
Just for the record, you cannot use edited formats in computations. The character "Z" in the PICTURE string of both variables turns the variable to be not considered numeric by the compiler.
You indeed have to declare a full numeric variable (no formatting) and move its result value to the formatted variable after.
Resolved this by useing two temp variables not sure if this is the way to do it but it worked.
MOVE ANNUAL-SALARY TO OLD-SALARY
MULTIPLY ANNUAL-SALARY BY .07 GIVING TEMP
ADD ANNUAL-SALARY,TEMP TO TEMP2
MOVE TEMP2 TO NEW-SALARY
MOVE ZEROS TO TEMP, TEMP2
MOVE UNION-DUES TO OLD-DUES
MULTIPLY UNION-DUES BY .04 GIVING TEMP
ADD UNION-DUES,TEMP TO TEMP2
MOVE TEMP2 TO NEW-DUES
MOVE ZEROS TO TEMP, TEMP2
MOVE INSURANCE TO OLD-INSURANCE
MULTIPLY INSURANCE BY .03 GIVING TEMP
ADD INSURANCE,TEMP TO TEMP2
MOVE TEMP2 TO NEW-INSURANCE
MOVE ZEROS TO TEMP, TEMP2
In addition to the COMPUTE suggested by NealB:
MOVE ANNUAL-SALARY TO OLD-SALARY
MULTIPLY ANNUAL-SALARY BY 1.07 GIVING NEW-SALARY
MOVE UNION-DUES TO OLD-DUES
MULTIPLY UNION-DUES BY 1.04 GIVING NEW-DUES
MOVE INSURANCE TO OLD-INSURANCE
MULTIPLY INSURANCE BY 1.03 GIVING NEW-INSURANCE
The use of TEMP2 in the code shown is particularly bad. It firstly relies on its initial value (presumably a VALUE clause) and for the second time through relies upon the 'MOVE ZEROS TO TEMP, TEMP2' at the end of the block. That's a bad way to do it. The repeated initialisation of TEMP is pointless, as TEMP is always the "target" of a GIVING, so its value at the time of the MULTIPLY is irrelevant.
Related
I have a numeric variable (year of birth) in SPSS and i would like to take the last for digits out of it. Most values are like 1988, 2001, 1948 etc. But about 250 respondents entered their year of birth like 30-2-1947, or 2-9-1984 etc. That means not all values have the same length. By taking the last 4 digits into a new variable I could create an age category for all the respondents.
How can I do that?
I tried by converting the variable to a string and using substr to get a part of the value, but I always had to choose a starting point. I want to start from the last digit and then move backwards.
Instead of using SUBSTR() you can try using RIGHT() to grab the last four digits.
* convert yob to string variable.
ALTER TYPE yob (A10).
EXE .
* use RIGHT to extract the last 4 digits and convert to numeric.
COMPUTE n_yob = NUMBER(RIGHT(yob, 4, F4)) .
EXE .
You can now use n_yob to calcuate age (ex: COMPUTE age = 2022-n_yob .). You can also use ALTER_TYPE again if you want to convert yob back to it's original type.
I'm making the "99 Bottles" program, but with user input on how many to take down. I'm very new to COBOL and I'm definitely overlooking something simple or just completely thinking about this the wrong way.
The following is what I currently have:
IDENTIFICATION DIVISION.
PROGRAM-ID. HW.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 COUNTER PIC S99.
01 BOTTLES PIC Z9.
01 BOTTLES-REMAINING PIC Z9.
01 NUM PIC s9(02) VALUE 0.
PROCEDURE DIVISION.
PERFORM VARYING COUNTER FROM 99 BY NUM UNTIL COUNTER = 0
DISPLAY "How many bottles would you like to take down?"
ACCEPT NUM
MOVE COUNTER to Bottles
subtract NUM FROM COUNTER GIVING BOTTLES-REMAINING
DISPLAY SPACES
EVALUATE COUNTER
WHEN 1
DISPLAY " 1 bottle of beer on the wall, "
" 1 bottle of beer."
DISPLAY "Take one down and pass it around, "
"no more bottles of beer on the wall."
WHEN 2 Thru 99
DISPLAY BOTTLES " bottles of beer on the wall, "
BOTTLES " bottles of beer."
DISPLAY "Take one down and pass it around, "
BOTTLES-REMAINING
" bottles of beer on the wall."
END-EVALUATE
END-PERFORM
GOBACK.
I need to make the NUM clause negative in the following statement (or the data division) so it will subtract from the counter:
PERFORM VARYING COUNTER FROM 99 BY NUM UNTIL COUNTER = 0
I see a few issues here.
First, and this is from admittedly faded memory, but I seem to recall that the VARYING clause required a constant value for the delta. I don't think you can use an actual changing NUM to do this.
So your loop would be better off not using the VARYING clause and instead be something like (code here may not be syntactically correct COBOL, it's meant more to show intent and/or method):
set counter to 99
perform until counter = 0
blah blah blah then change counter
end perform
Second, your little ditty doesn't make sense any more if you're allowed to remove more than one bottle at a time. The statements for the third stanza of the rhyme should be modified similarly to the bottles-left stanza:
evaluate num
when 1
display "Take one down and pass it around, "
when 2 thru 99
display "Take ", num, " down and pass them around, "
end evaluate
And, finally, you probably want to avoid the situation where you remove more bottles than you have available (or less than one, for that matter). That can be done by silently enforcing those limits (clamping) immediately after getting the user input:
accept num
if num is less than one
set num to one
end if
if num is greater than counter
set num to counter
end if
You could also complain and require the user to enter a valid quantity but the easiest solution is probably just to clamp it.
I am not able to understand what is the logic behind these lines:
COMPUTE temp = RESULT - 1.843E19.
IF temp IS LESS THAN 1.0E16 THEN
Data definition:
000330 01 VAR1 COMP-1 VALUE 3.4E38. // 3.4 x 10 ^ 38
Here are those lines in context (the sub-program returns a square root):
MOVE VAR1 TO PARM1.
CALL "SQUAREROOT_ROUTINE" USING
BY REFERENCE PARM1,
BY REFERENCE RESULT.
COMPUTE temp = RESULT - 1.843E19.
IF temp IS LESS THAN 1.0E16 THEN
DISPLAY "OK"
ELSE
DISPLAY "False"
END-IF.
These lines are just trying to test if the result returned by the SQUAREROOT_ROUTINE is correct. Since the program is using float-values and rather large numbers this might look a bit complicated. Let's just do the math:
You start with 3.4E38, the squareroot is 1.84390889...E19.
By subtracting 1.843E19 (i.e. the approximate result) and comparing the difference against 1.0E16 the program is testing whether the result is between 1.843E19 and 1.843E19+1.0E16 = 1.844E19.
Not that this test would not catch an error if the result from SQUAREROOT_ROUTINE was too low instead of too high. To catch both types of wrong results you should compare the absolute value of the difference against the tolerance.
You might ask "Why make things so complicated"? The thing is that float-values usually are not exact and depending on the used precision you will get sightly different results due to rounding-errors.
well the logic itself is very straight forward, you are subtracting 1.843*(10^19) from the result you get from the SQUAREROOT_ROUTINE and putting that value in the variable called temp and then If the value of temp is less than 1.0*(10^16) you are going to print a line out to the SYSOUT that says "OK", otherwise you are going to print out "False" (if the value was equal to or greater than).
If you mean the logic as to why this code exists, you will need to talk to the author of the code, but it looks like a debugging display that was left in the program.
Given the following code:
IDENTIFICATION DIVISION.
PROGRAM-ID. FABS.
ENVIRONMENT DIVISION.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 NUM PIC 9 VALUE ZEROS.
01 ABSVAL PIC 99 VALUE ZEROS.
PROCEDURE DIVISION.
PROGRAM-BEGIN.
DISPLAY "This program returns the absolute value of a number.".
DISPLAY SPACE.
DISPLAY "Input value: " WITH NO ADVANCING.
ACCEPT NUM.
IF (NUM = -0) THEN
COMPUTE ABSVAL = 0
ELSE
IF (NUM > 0) THEN
COMPUTE ABSVAL = 0
ELSE
COMPUTE ABSVAL = ABSVAL * -1
END-IF
END-IF.
DISPLAY "|", NUM "| = ", ABSVAL.
PROGRAM-DONE.
STOP RUN.
Why is the output zero? Is there something wrong? And how do you make a signed/negative input?
Thinking of your task, rather than why you get zero, it is easy.
Let's assume you get a signed value with your ACCEPT.
01 value-from-accept PIC S9.
01 absolute-value-for-output PIC 9.
MOVE value-from-accept TO absolute-value-for-output
DISPLAY
"|"
value-from-accept
"| = "
absolute-value-for-output
You may think that something is wrong with the output from value-from-accept (depending on compiler) but you can always MOVE it to a numeric-edited field and DISPLAY that.
Tip: To reverse the sign of a signed field.
SUBTRACT field-to-reverse-sign
FROM ZERO
GIVING the-reversed-field
SUBTRACT is faster than MULTIPLY.
You have defined your field which is ACCEPTed as unsigned.
The first two "legs" of your nested-IF set ABSVAL to zero. The remaining leg takes the existing value of ABSVAL (from the VALUE ZEROS, so it is zero) and multiplies it by minus one. Getting -ve zero (possibly), but then storing it in an unsigned field. So ABSVAL will always be zero when you come to the DISPLAY.
You define a signed field by prefixing the PICture string with an S:
01 a-signed-field PIC S9(5).
Depending on your compiler, you can type a - when entering the data and it'll be held happily as a negative value in a signed field (which you have to define) or you have to code for it yourself.
after your correction above
I am not sure how you are testing it but to just to ensure that the values are stored correct you may want to have both the fields signed i.e. pic S9 or pic S99. Its possible that without the preceding S (sign) the variables are not really storing the negative sign regardless of what the screen is showing.
pls observe what results you get then
I have this Cobol paragraph that will search one table which at this point in my example would have a table counter of 2 which is what the first INDEX loop does. The variable A represents an Occurs that is defined in a file (include) which has 5 occurrences. I can get to the if statement but it returns false. I read the information out of a ParmCard and store that in the table which is Table-B and the ParmCard is correct.
I did get it to find one value when was changing values around (conditional statements) but I know that both of the values that it is looking for in the ParmCard are in the file and should be found and it should find two results. I would have tried Expeditor but the system was down at work.
Is there something wrong with the index or may be I think that the perform's are working one way but they are really working a different way? This Search paragraph gets executed with every read of the ID file thus it will look in the table as many times as the ID file has an ID and ID symbols are unique.
Question: Why would the IF-STATEMENT not be working?
Code:
SEARCH-PARAGRAPH.
PERFORM VARYING SUB FROM 1 BY 1 UNTIL SUB > 2 <--DUPLICATE INDEXER
IF A(TAB) = TABLE-B(SUB) THEN
MOVE 6 TO TAB
MOVE 'TRUE' TO FOUND-IS
PERFORM WRITE-FILE THRU X-WF
PERFORM LOG-RESULT THRU X-LR
END-IF
END-PERFORM
X-SP. EXIT.
SEARCH-INDEX.
PERFORM VARYING I FROM 1 BY 1 UNTIL I > 2
DISPLAY 'INDEX --> ' I
PERFORM VARYING TAB FROM 1 BY 1 UNTIL TAB > 5
DISPLAY 'TAB --> ' TAB
PERFORM SEARCH-PARAGRPAH THRU X-SP
END-PERFORM
END-PERFORM.
X-SEARCH-INDEX. EXIT.
Here is the way that it works now and I do get the results I want. It is difficult to past the company code up because you never know who might have a problem.
New Code:
READ-PROV.
READ P-FILE
AT END
MOVE 'Y' TO EOF2
GO TO X-READ-PROV
NOT AT END
ADD 1 TO T-REC-READ
MOVE P-RECORD TO TEST-RECORD
PERFORM CHECK-MATCH THRU X-CHECK-MATCH
END-READ.
X-READ-PROV. EXIT.
CHECK-MATCH.
PERFORM VARYING SUB FROM 1 BY 1 UNTIL SUB > TABLECOUNTER
IF PID >= FROM(SUB) AND
PID <= THRU(SUB) THEN
IF TODAY < P-END-DTE THEN
IF TOTAL-PD = 0 AND
TOTAL-PD = 0 AND
TOTAL-PD = 0 AND
TOTAL-PD = 0 AND
TOTAL-PD = 0 THEN
IF PBILLIND NOT EQUAL 'Y'
PERFORM VARYING TAB FROM 1 BY 1 UNTIL TAB > 5
IF P-CD(TAB) = TY(SUB) THEN
MOVE 6 TO TAB
DISPLAY('***Found***')
ADD 1 TO T-REC-FOUND
END-IF
END-PERFORM
END-IF
END-IF
END-IF
END-IF
END-PERFORM.
X-CM. EXIT.
We can't tell.
There is nothing "wrong" with your nested PERFORM. The IF test is failing simply because it is never true.
We can't get you further with that without seeing your data-definitions, sample input and expected output.
However... my guess would be that the problem is with your data from the PARM in the JCL. That is the most likely area.
It is of course possible that the problem is with the other definition.
A couple of things whilst waiting.
Please always post the actual code, always. We don't want to look for errors in what you have typed here, we want to see the actual code. You have not shown the actual code, because it will not compile, as INDEX is a Reserved Word in COBOL, so you can't use it for a data-name.
Please always bear in mind that what you think may be wrong may not be the problem, so post everything we are likely to need (data-definitions, data you used, actual results you got with the code (including anything you've added for problem-determination), results which were expected).
Some tips.
A paragraph requires a full-stop/period after the paragraph-name and before the next paragraph. If you put that second full-stop/period on a line of its own, and have no full-stops/periods attached to your PROCEDURE code itself, you'll make things look neater and avoid problems when you want to copy some lines which happen to have a full-stop/period to a place where they cause you a mess.
You are using literal values. This is bad. When the number of entries in one of your tables changes, you have to change those literal values. Say the 2 needs to be changed to 5. You have to look at every occurrence of the literal 2 and decide if it needs to be changed. Then change it to 5. Then you get another request, to change the table which originally had five entries so that it will have six. See how difficult/error-prone life can be?
If instead you have unique and well-named data-names for your maximum number of entries, you only have one place to make a change, and you know it can be changed without reference to the rest of the code (assuming someone clever hasn't seen it has a value they want for something, and use it despite its name, of course...).
The content of those fields you can set automatically:
01 TABLE-1.
05 FILLER OCCURS 2 TIMES.
10 A PIC X(10).
01 TABLE-2.
05 FILLER OCCURS 5 TIMES.
10 TABLE-B PIC X(10).
01 TABLE-1-NO-OF-ENTRIES COMP PIC 9(4).
01 TABLE-2-NO-OF-ENTRIES COMP PIC 9(4).
...
PROCEDURE DIVISION.
...
COMPUTE TABLE-1-NO-OF-ENTRIES = LENGTH OF TABLE-1
/ LENGTH OF A
COMPUTE TABLE-2-NO-OF-ENTRIES = LENGTH OF TABLE-2
/ LENGTH OF TABLE-B
DISPLAY TABLE-1-NO-OF-ENTRIES
DISPLAY TABLE-2-NO-OF-ENTRIES
That gives you the output 2 and 5.
The names I've used are a mixture of yours and some for demonstration purposes only. Make everything meaningful, and by that I don't mean trite, as my example names would be in real life.
If you insist on escaping from within your PERFORM like that (and take note of Bruce Martin's comment), you can calculate your escape value by using new, aptly-named, fields and giving them the value of the above plus one.
To do a nested loop when the outer loop only has two entries is overkill. You don't need to escape out of the loops like you do, if you have a termination condition on the loop.
That'll do for now until we see your definitions, data and results.