Monday, May 23, 2022

Principle of greatest astonishment

PL/I is a very forgiving language—the programmer can code nearly every type of operand for an operation and have it compile and (sometimes) execute. For example, they can add a fixed-point numeric value, a bit string, and a character string.  [e.g. 3.5 + '10'b + '  433E5']  While not producing the best code, this will operate correctly, as long as the character string represents a valid number. Occasionally, however, PL/I can produce completely unexpected results. 

One case of this is conversion of a numeric value to a bit string. The results, although fully documented, were so surprising that I checked the code with two compilers to make sure it wasn't an error.

The two cases I tried assigned values to a string declared as BIT(8). The first case assigned a variable declared as FIXED BINARY(7), and the second case assigned a numeric constant, by definition FIXED DECIMAL.

CASE 1
         DECLARE  bt BIT(8);  
    DECLARE  fx FIXED BINARY(7);
    fixed = 3;
    bt    = fx.

The result is 6!

CASE 2:
    DECLARE bt BIT(8);
    bt = 3;

In this case, the result is 48! What is going on here?

As I said, this is completely documented.

In Case 2, "3" is a FIXED DECIMAL number with precision (1,0). Before converting it to BIT, it is first converted to FIXED BINARY. The precision of this value is CEIL( (1-0)*3.32 ), or 4. This converts to a bit string of length four, containing '0011'b. This string is then assigned, left justified—it is a string, after all—to bt, giving a result of  '0011_0000'b, hex 30 or decimal 48.

In Case 1, fx is already FIXED BINARY(7,0) with a value of 3. UNSPEC(fx) is '0000_0011'b, but aha! the sign bit is dropped, so the source bit string is '0000011'b. This is then assigned to bt, again left justified, giving a result of '0000_0110'b, hex 06 or decimal 6.

ADDENDUM:
In case 1, if fx is declared FIXED BINARY(8) UNSIGNED, then bt will be 3, since there is no need to account for a sign bit.

Enjoy!