Monday, February 20, 2012

z/OS float numerics and IEEE754


On z/OS, float and double numerics have been available for a long time but are seldom used with languages such as COBOL. The reason is that COBOL is primarily used for business rather than scientific applications and accountants prefer fixed decimal numerics.

In the java and C/C++ worlds though, floats and doubles are often used, even in business applications.

For COBOL to Java integration it is therefore important to understand how such numerics can be interchanged.

z/OS does not encode float and double data items in the usual IEEE754 way expected by Java and C/C++ developers.

To illustrate the differences lets use a COBOL program with a data item defined like this:

   01 W-ZOS-FLOAT  USAGE COMP-1 VALUE -375.256.

If we look at the content of this data item, it's hexadecimal representation is:

   X'C3177419'

or in binary:

   11000011 00010111 01110100 00011001
   ^^-----^ ^------------------------^
   |   |               |-mantissa
   |   |-biased exponent
   |-sign

The sign bit is turned on as we have stored a negative number. This is pretty much the only thing that is common with IEEE754.

The biased exponent is stored on 7 bits. The bias is X'40' (decimal 64). In our case, the decimal value stored in the biased exponent is 67, therefore the exponent is 67 - 64 (bias) = 3. Beware that this is an hexadecimal exponent, not a decimal one.

Finally, the content of the last 3 bytes is the mantissa, in our case: X'177419'. Now, since the exponent is 3, the integer part is X'177' (375 decimal as expected). The fractional part, X'419' is trickier to convert back to decimal. This is because most calculators have decimal to hexadecimal converters that do not work on fractions.

X'419' should be interpreted as 4 * (16**-1) + 1 * (16**-2) + 9 * (16**-3). You can use the calculator again if you observe that multiplying by 16**3, you get 4 *(16**2) + 1 * (16**1) + 9 * (16**0). In other words you can divide X'419'(decimal 1049) by 16**3 (decimal 4096) and you get 0,256103515625 which is our fractional part in decimal.

In summary, z/OS float items are Hexadecimal-based with a 7 bit exponent and 24 bit mantissa.

By contrast, IEEE754 floats are binary based, with an 8 bit exponent and 23 bit mantissa (called significand in the distributed world).

The internal representation of our -375.256 value is therefore:

   X'C3BBA0C5'

or in binary:

   11000011 10111011 10100000 11000101
   ^^-------^^-----------------------^
   |   |               |-mantissa
   |   |-biased exponent
   |-sign

Sign digit is same as z/OS as already mentioned.

The 8 bit biased exponent is 10000111 or 135 decimal. Float exponents in IEEE754 have a 127 decimal bias, the exponent is therefore 135 - 127 (bias) = 8. Beware that this is a binary exponent not a decimal one.

The 23 bit mantissa is 01110111010000011000101, BUT, there is an implied 1 as the most significant bit (msb). The real mantissa is therefore: 101110111010000011000101.

The integer part starts right after the implicit msb. Since we have an 8 exponent, the integer part is: 101110111 or 375 decimal.

Now for the fractional part, 010000011000101, again you can use a regular calculator which gives a decimal value of 8389 but you must divide that by 2**15 (23 bits - 8 exponent bits) or 8389 / 32768 = 0,256011962890625 which is our fractional part in decimal.

As you can see, although z/OS floats and IEEE754 floats are both 4 bytes long, they store numbers in quite a different way so don't attempt to push Java floats directly to COBOL buffers!