I have the below Working-storage variable in my program.
01 W-WRK.
02 W-MNTH-THRSHLD PIC S9(04).
I am using the below COMPUTE function to negate the value of W-MNTH-THRSHLD.
COMPUTE W-MNTH-THRSHLD OF W-WRK =
W-MNTH-THRSHLD OF W-WRK * -1.
I want to know if this approach is right or is there any alternative for the same?
Firstly, why are you using qualification (the OF)? That is only required if you have defined duplicate names. Why define duplicate names in the WORKING-STORAGE?
Secondly, unless you are using a very old COBOL compiler, you should only use the minimum required full-stops/periods in the PROCEDURE DIVISION. That is, one to terminate the paragraph/SECTION label, one to terminate a paragraph/SECTION. One to terminate the PROCEDURE DIVISION header. One to terminate a program (if a full-stop/period is not already there. Keeping extra full-stops/periods around makes it more difficult to copy code around. Put the full-stop/period on a line of its own, so no line of code has one, then you can't accidentally terminate a scope by copying a line of code with a full-stop/period to within a scope.
With those in mind, your code becomes:
COMPUTE W-MNTH-THRSHLD = W-MNTH-THRSHLD
* -1
Multiplication is slower than subtraction. So as Bruce Martin suggested:
COMPUTE W-MNTH-THRSHLD = 0
- W-MNTH-THRSHLD
I do it like this:
SUBTRACT W-MNTH-THRSHLD FROM 0
GIVING W-MNTH-THRSHLD-REV-SIGN
I dislike "destroying" a value just for the heck of it. If the program fails, I know what W-MNTH-THRSHLD contained, plus the meaningful name for the target field explains what the line does.
You could also DIVIDE (or / in COMPUTE), but that is even slower than MULTIPLY (or *).
Also bear in mind that conversions may be required, because you are doing arithmetic with a USAGE DISPLAY field. If you define your field as BINARY or PACKED-DECIMAL conversion is less likely for arithmetic. You won't lose by doing that, unless your compiler can deal with a USAGE DISPLAY in arithmetic without requiring conversion.
Note also, COMPUTE is not a function. COMPUTE is a verb, just a part of the language. "I am using COMPUTE" is sufficient, and not even necessary, as we can see that from the code.
Related
Why such code is used in some applications instead of a MOVE?
add 16 to ZERO giving SOME-RESULT
I spotted this in professionally written code at several spots.
Sorce is on this page
Why such code is used in some applications instead of a MOVE?
add 16 to ZERO giving SOME-RESULT
Without seeing more of the code, it appears that it could be a translation of IBM Assembler to COBOL. In particular, the ZAP (Zero and Add Packed) instruction may be literally translated to the above instruction, particularly if SOME-RESULT is COMP-3. Thus, someone checking the translation could see that the ZAP instruction was faithfully translated.
Or, it could be an assembler programmer's idea of a joke.
Having seen the code, I also note the use of
subtract some-data-item from some-data-item
which is used instead of
move zero to some-data-item
This is consistent with operations used with packed decimal fields in IBM Assembly, where there are no other instructions to accomplish "flexible" moves. By flexible, I mean that the packed decimal instructions contain a length field so that specific size MVC instructions need not be used.
This particular style, being unusual, may be related to catching copyright violations.
From my experience, I'm pretty sure I know the reason why the programmer would have done this. It has something to do with the binary representation of the number.
I bet SOME-RESULT is a packed-decimal (or COMP-3) format number. Let's assume the field is defined like this
05 SOME-RESULT PIC S9(5) COMP-3.
This results in a 3-byte field with a hex representation like this
x'00016C'
The decimal number is encoded as a binary encoded decimal (BCD, one decimal digit per half-byte), and the last half-byte holds the sign.
Let's take a look at how the sign is defined:
if it is one of x'C', x'A', x'F', x'E' (café), then the number is positive
if it is one of x'B', x'D', then the number is negative
any of x'0'..'x'9' are not valid signs, so we can distinguish signed packed-decimals from unsigned.
However, a zoned number (PIC 9(5) DISPLAY) - as in the source code - looks like this:
x'F0F0F0F1F6'
As you can see, each decimal digit is an EBCDIC character with the 'zone' part (the first half-byte) always being x'F'.
Now we get closer to your question!
What happens when we use
MOVE 16 TO SOME-RESULT
If you just MOVE a number to such a field, this results in being compiled into a PACK instruction on the machine code level.
PACK SOME-RESULT,=C'16'
A pack instruction takes a zoned number and packs it by picking only the second half-byte of each byte and storing it in the half-bytes of the packed number - with one exception! When it comes to the last byte, it simply flips the two half-bytes and stores them in the last half-byte of the decimal.
This means that the zone of the last byte of the zoned decimal becomes the sign in the packed decimal:
x'00016F'
So now we have an x'F' as the sign – which is a valid positive sign.
However, what happens if use this Cobol instruction instead
ADD 16 TO ZERO GIVING SOME-RESULT
This compiles into multiple machine level instructions
PACK SOME_RESULT,=C'0'
PACK TEMP,=C'16'
AP SOME_RESULT,TEMP
(or similar - the key point is that is needs an AP somewhere)
This makes a slight difference in the result, because the AP (add packed) instruction always sets the resulting sign to either x'C' for a positive or x'D' for a negative result.
So the difference lies in the sign
x'00016C'
Finally, the question is why would one make this difference? After all, both x'F' and x'C' are valid positive signs. So why care?
There is one situation when this slight difference can cause big problems: When the packed decimal is part of an index key, then we would not get a match, even though the numbers are semantically identical!
Because this situation occurred quite often in older databases like VSAM and DL/I (later: IMS/DB), it became good practice to "normalize" packed decimals if they were part of an index key.
However, some programmers adopted the practice without knowing why, so you may come across code that uses this "normalization" even though the data are not used for index keys.
You might also wonder why a compiler does not optimize out the ADD 16 TO ZERO. I'm pretty sure it once did, but that broke a lot of applications, so this specific optimization was removed again or at least made a non-default option with warnings.
Additional useful info
Note that at least the Enterprise Cobol for z/OS compiler allows you to see exactly the machine code that is produced from your source code if use the LIST compile option (see this example output). I recommend to always compile with options LIST, MAP, OFFSET, XREF because these options enable you find the exact problem in your Cobol source even when you only have a program dump from an abend.
Anyway, good programming practice is not to care about the compiler or the machine code, but about the other programmers who will have to maintain, and thus read and understand the code. Good practice would be to always prefer simple and readable instructions, and to document the reasons (right in the code) when deviating from this rule.
Some programmers like to do things "just because they can". I have a feeling that is what you are seeing here. It makes about as much sense as doing
a := 0 + b
would in go.
How does the computer execute value equality comparisons? Does it compare the values bit by bit, starting from the smallest bit, and stop once two different bits are encountered? Or does it start from the highest bit? Does it go through all bits regardless of where/when two unlike bits are found?
When you write an equality comparison in a higher level language (e.g. c), it will be transformed to intermediary representation and then to the instructions of a particular platform this code will be executed upon. Compiler is free to implement the equality comparison using any of the instructions available on a target architecture. The idea is usually to make it faster.
Different architectures have different instruction sets. Different processors can have varying implementation strategies (again to make things faster), as long as they comply with the spec.
Below are a few examples
x86
CMP command is used to compare two values. Here's an excerpt from Instruction set reference.
Compares the first source operand with the second source operand and sets the status flags in the EFLAGS register according to the results. The comparison is performed by subtracting the second operand from the first operand and then setting the status flags in the same manner as the SUB instruction. When an immediate value is used as an operand, it is sign-extended to the length of the first operand.
This basically means all bits are examined. I guess it was implemented that way to allow non-equality(<,>) comparisons too.
So all bits are examined. In the simplest cases that can be done serially, but can be done faster. See wikibooks on add / subtract blocks.
ARM
TEQ command can be used to test two values for equality.
Here's an excerpt from the infocenter.arm.com
The TEQ instruction performs a bitwise Exclusive OR operation on the value in Rn and the value of Operand2. This is the same as the EORS instruction, except that it discards the result.
Use the TEQ instruction to test if two values are equal without affecting the V or C flags.
Again all bits are examined.
Studying for a test right now and can't seem to wrap my head around when to use "V" for a decimal instead of an actual decimal in PIC clauses. I've done some research but can't find anything I understand. Only been learning cobol for about a week, so is there like a rule of thumb here? Thanks for your time.
You use an actual decimal-point when you want to "output" a value which has decimal places, like a report line, a position on a screen, an item in an output file which is going to a "different" system which doesn't understand the format with an implied decimal pace.
That's what the V is, it is an implied decimal place. It tells the compiler where to align results from calculations, MOVEs, whatever. Computer chips, and the machine instructions they support, don't know about actual decimal points for their internal processing.
COBOL is a language with fixed-length fields. The machine instructions don't need to know where the decimal point is (effectively it can deal with everything as integer values) but the compiler does, and the compiler has to do the correct scaling and alignment of results.
Storing on your own files, use V, the implied decimal place.
For data which is to be "human readable" or read by a system which cannot understand your character set, cannot scale what looks like an integer, use an actual decimal-point, . (for computer-readable stuff, you can sometimes use a separate scaling factor, if that is more convenient for the receiving system).
Basically, V for internal, . for external, should be a rule of thumb to get you there.
Which COBOL are you using? I'm surprised it is not covered in your documentation.
Why would this if statement below need a NEXT SENTENCE because there is a statement in both the IF and the ELSE part of the statement.
Question: Why is this an error in the if statement.
CHECK-PARM.
IF NAME = 'SW89JS' THEN 1183
E-NAME = 'FALSE'
Expected a verb or "NEXT SENTENCE", but found "E-NAME". The statement was discarded.
ELSE
E-NAME = 'TRUE'
"E-NAME" was invalid. Skipped to the next verb, period or procedure-name definition.
P-NAME = 'SW89JS'
END-IF.
Since it is somewhere buried in this answer, I'm going to repeat it up hear, even expand it a little.
You have a value you are testing. From the name it likely comes from the PARM on the EXEC card in the JCL.
You test the value, set a flag (TRUE/FALSE literals) on the result of the value, and use it later.
With an 88 you can make that parm value into the flag itself.
01 NAME PIC X(6).
88 IT-IS-SW89JS VALUE "SW89JS".
Now you can never get your flags out of step, as you only have one flag. One fewer flag to understand and potentially get wrong.
Because we don't have assignments in COBOL.
MOVE 'TRUE' TO E-NAME
or
MOVE data-name-with-value-true TO E-NAME
or
01 FILLER PIC X VALUE SPACE.
88 IT-IS-SW89JS VALUE "Y".
SET IT-IS-SW89JS TO TRUE
or
01 NAME PIC X(6).
88 IT-IS-SW89JS VALUE "SW89JS".
And with the last forget about anything else.
COBOL is not like many other languages. No strings, as you may know them. No arrays, as you may know them. No assignments. No booleans. No user-written functions. Few Intrinsic Functions. No public function libraries. It does have some other stuff :-)
A couple of points from the comments.
COBOL is a language of fixedness. Fixed-length field, fixed-length tables. The length of the data in a 30-byte field is 30 bytes. The length of the content of the field, in terms of what the data represents, is something the programmer has to work out, if needed. Mostly we don't need it, so we don't have to work it out.
The fixedness also imposes limits. So we think of ways to do things differently, so we don't have a limit, waiting to bust, dangling over our heads. We don't just pick a function which looks like maybe it makes life easy for us (less code to write) regardless of how it carries out the task. Usually we don't have a function anyway, and we write specific code to be re-used, through a CALL, for a specific system or set of systems.
A COBOL program may take longer to write (I say may because 90+% of the time it is a question of starting out by copying a program which is close to what you want, and then making it specific) but that program may have a lifetime of 30 years. It may be changed many times during its life. It may never be changed, but need to be understood many times during that period.
Conceptually, COBOL is a very different language from those with assignments/strings/arrays. If you are supposed to pick up COBOL with no training, there will be many pitfalls.
Yes, Bruce Martin, I suppose COBOL does have an assignment: the COMPUTE. The left-side can only be numeric or numeric-edited, although there can be multiple fields, and the right-side can only have numerics (or intrinsic functions returning numerics). It only supports basic mathematical operators (+, -, , /, *). It does allow rounding of the final answer if desired, and also allows for interception of "overflow" (ON SIZE ERROR).
It can be used as a simple assignment:
COMPUTE A = B
This will generate the same code as:
MOVE B TO A
Some people do this, though I've never really worked out why. There is a rumour that it means you can use ON SIZE ERROR (and don't forget END-COMPUTE if you do use it) to trap an overflow.
However, I always make my fields big enough, or deliberately truncate when that is the result I want, so I don't really get that.
In short, welcome to COBOL. Don't expect it to be like any other language you've used.
As Bill stated the problem is:
E-NAME = 'FALSE'
In Cobol (unlike most other languages), each statement starts with a control word
e.g.
Compute abc = 123
Move 'FALSE' to E-NAME
Perform abc
Call 'xyz'
With Cobol the control word on the far left of a line tells you what the statement is doing.
Also as Bill stated, in Cobol a boolean is normally define using 88 levels:
01 FILLER PIC X VALUE SPACE.
88 IS-ENAME VALUE "Y".
88 ENAME-OFF VALUE "N".
and your code becomes
IF NAME = 'SW89JS' THEN
Set ENAME-OFF to true
ELSE
Set IS-ENAME to true
Move 'SW89JS' to P-NAME
END-IF.
I always thought it was part of the design philosophy in Pascal, that it looked at both the right and left hand sides of an expression when deciding what format/precision to use for an operation. So that, unlike C where an expression like,
Float_Var = 1/3
results in a value of 0.0 for Float_Var, Pascal always gets this stuff right. :)
So I was kind of surprised when I went to multiply two LongInts (32 bit) to give an Int64 result and found I was getting anomalous results. I had to get all C like and use,
Int64_Var := Int64(LongIntVar1) * LongIntVar2
to make it work correctly. (BTW. This was under Delphi, various versions tested, but all win32).
I was just wondering if this is an exceptional case in Delphi/Pascal? Or are there other examples where the usual Pascal way, using the types on both sides of an expression to decide on how the operation is performed, doesn't hold.
If by "both sides" you mean that it looks at the type of the target variable in an assignment for determining the expression type, then no, that has never been the case. Delphi works like any other mainstream compiler in that regard - that is, the type of an expression is determined from the inside out.
I always thought it was part of the design philosophy in
Pascal, that it looked at both the right and left hand sides of
an expression when deciding what format/precision to use
for an operation.
That is not correct. Expressions assignment targets do not influence the evaluation of the expression.
The reason that
Float_Var = 1/3;
evaluates to 0 in C/C++ is that the / operator is overloaded. It can mean either integer division or floating point division. If one of the arguments is floating point then the operator is floating point division, otherwise, as here, it is integer division.
In Delphi the / operator is not overloaded. It is always floating point division. That's why this code gives a compile error:
Int_Var := 1/3;