how to display DEC floating point format given 32 bits in IEEE standard

0

I'm currently given 32 bits of data that are in the Digital Equipment Corporation (DEC) floating point format or PDP-11 (or fp-11). The data is given in little endian. In C, how do I get a regular IEEE-754 single precision floating point from it?

I've found some references, but they are very confusing: http://home.kpn.nl/jhm.bonten/computers/bitsandbytes/wordsizes/hidbit.htm

http://www.bitsavers.org/pdf/dec/pdp11/1160/EK-FP11E-UG-001_FP11-E_Users_Guide_Dec77.pdf

EDIT: (in response to comment) by confusing I'm referring to the msb and lsb aspects of conforming to little endian and additionally how to get that into a human readable format for printf. heres what i tried so far:

float getLatitude(){
    uint16_t lat1 = ((0x0000 | rx_data[32]) << 8)| rx_data[31];
    uint16_t lat2 = ((0x0000 | rx_data[34]) << 8)| rx_data[33];
    uint32_t lat = (0x00000000 | lat1 << 16) | lat2;
    uint8_t ex = (lat >> 23) & 0xFF;
    uint32_t frac = lat & 0x7FFFFF;
    float latitude = 
    if(((lat & 0x80000000) >> 31) == 1){latitude = latitude * -1}
    return latitude;
}
c
floating-point
ieee-754
pdp-11
asked on Stack Overflow Nov 9, 2020 by Robert Pink • edited Nov 9, 2020 by Robert Pink

1 Answer

1

Caveat: I have never used a PDP-11, so the following is based purely on the documentation linked in the question.

The PDP-11 was a 16-bit machine using 16-bit words. 32-bit single-precision floating-point data was stored in a "mixed-endian" format: the more significant word was stored at the lower address, but within each word, the less significant byte was stored at the lower address. In other words, in order of increasing addresses, the four bytes are stored in order 2, 3, 0, 1.

The PDP-11 floating-point format is similar to the IEEE-754 binary32 format, using sign-magnitude representation with eight exponent bits and a significand (mantissa) whose most significant bit is assumed to be 1 and therefore not stored. The exponent bias is 128 instead of 127 for IEEE-754 binary32, and the significand is normalized to [0.5, 1) instead of [1, 2) for IEEE-754 binary32. Also, subnormals, infinities, and NaNs are not supported.

This means that conversion can never overflow, but it can underflow (with an accompanying potential reduction in accuracy) to an IEEE-754 binary subnormal. The following program assumes that we are running on a machine with IEEE-754 floating-point, and are presented with PDP-11 floating-point data as a sequence of bytes in memory order.

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <limits.h>
#include <math.h>

float uint32_as_float (uint32_t a)
{
    float r;
    memcpy (&r, &a, sizeof r);
    return r;
}

float single_precision_pdp11_to_ieee754 (uint8_t *data)
{
    /* mixed-endian: more significant word at lower address,
       but within word less significant byte at lower address
    */
    uint32_t raw = (((uint32_t)data[0] << 2 * CHAR_BIT) |
                    ((uint32_t)data[1] << 3 * CHAR_BIT) |
                    ((uint32_t)data[2] << 0 * CHAR_BIT) |
                    ((uint32_t)data[3] << 1 * CHAR_BIT));
    uint32_t expo = (raw >> 23) & 0xff;
    float res;

    if (expo == 0x00) {
        res = copysignf (0.0f, uint32_as_float (raw));
    } else if (expo == 0xff) {
        raw = raw - (2 << 23); // decrement exponent by 2
        res = uint32_as_float (raw);
    } else {
        res = 0.25f * uint32_as_float (raw);
    }
    return res;
}

int main (void)
{
    uint8_t data[11][4] = {{0xff, 0x7f, 0xff, 0xff}, // largest normal
                           {0x80, 0x40, 0x00, 0x00}, // 1.0f
                           {0x80, 0x00, 0x00, 0x00}, // smallest normal
                           {0x7f, 0x00, 0x00, 0x00}, // pseudo-zero
                           {0x00, 0x00, 0x00, 0x00}, // true zero
                           {0xff, 0xff, 0xff, 0xff}, // -largest normal
                           {0x80, 0xc0, 0x00, 0x00}, // -1.0f
                           {0x80, 0x80, 0x00, 0x00}, // -smallest normal
                           {0x7f, 0x80, 0x00, 0x00}, // -pseudo-zero
                           {0x00, 0x80, 0x00, 0x00}, // -true zero
                           {0x3F, 0x16, 0x9E, 0xB3}};// from question

    printf ("% 15.8e\n", single_precision_pdp11_to_ieee754 (data[0]));
    printf ("% 15.8e\n", single_precision_pdp11_to_ieee754 (data[1]));
    printf ("% 15.8e\n", single_precision_pdp11_to_ieee754 (data[2]));
    printf ("% 15.8e\n", single_precision_pdp11_to_ieee754 (data[3]));
    printf ("% 15.8e\n", single_precision_pdp11_to_ieee754 (data[4]));
    printf ("% 15.8e\n", single_precision_pdp11_to_ieee754 (data[5]));
    printf ("% 15.8e\n", single_precision_pdp11_to_ieee754 (data[6]));
    printf ("% 15.8e\n", single_precision_pdp11_to_ieee754 (data[7]));
    printf ("% 15.8e\n", single_precision_pdp11_to_ieee754 (data[8]));
    printf ("% 15.8e\n", single_precision_pdp11_to_ieee754 (data[9]));
    printf ("% 15.8e\n", single_precision_pdp11_to_ieee754 (data[10]));
    return EXIT_SUCCESS;
}

The output of the above program should look similar to this:

 1.70141173e+038
 1.00000000e+000
 2.93873588e-039
 0.00000000e+000
 0.00000000e+000
-1.70141173e+038
-1.00000000e+000
-2.93873588e-039
-0.00000000e+000
-0.00000000e+000
 3.87138358e-026
answered on Stack Overflow Nov 10, 2020 by njuffa • edited Nov 10, 2020 by njuffa

User contributions licensed under CC BY-SA 3.0