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;
}
rob ell
  • 45
  • 7
  • Define "confusing". Also show us what you've tried. – tadman Nov 09 '20 at 22:30
  • 1
    @tadman, I made an edit – rob ell Nov 09 '20 at 22:34
  • 1
    Nice, now there's something to review. What would help is some sample inputs and expected outputs as not all of us have a PDP sitting around. – tadman Nov 09 '20 at 22:35
  • the data its pulling from rx_data in its original order is: 3F 16 9E B3, what I would like to have as an output would be xx.xxx... – rob ell Nov 09 '20 at 22:39
  • I mean more like "I have `123.456789` which encodes as `0xFF38`" – tadman Nov 09 '20 at 22:45
  • I unfortunately can't see that as the hardware only outputs the raw binary over serial. All I know is it should read as radians in latitude – rob ell Nov 09 '20 at 22:48
  • Unless you have known inputs and outputs we're going to be as confused as you are. Just be sure to have things like +123.45678, -123.45678, +0.0, -0.0 and NaN and Infinity if necessary. – tadman Nov 09 '20 at 22:51
  • 1
    @tadman ok thanks for sticking with me, I'll try to do some trial and error stuff – rob ell Nov 09 '20 at 22:54
  • @RobertPink The PDP-11 used "mixed-endian" storage. It was a 16-bit machine with 16-bit words. The most significant word of a 32-bit single-precision number is stored at the lower address, followed by the least significant word. The 16-bit words themselves however are stored little-endian, i.e least significant byte at the lower address, followed by most significant byte at higher address. Make sure to combine the bytes in the correct order into a `uint32_t` prior to starting the conversion. – njuffa Nov 10 '20 at 17:34
  • in the title you said you have a 32-bit IEEE float and want to get the DEC version, and in the body you want to convert from the DEC to IEEE-754. What? – phuclv Nov 11 '20 at 01:54
  • @phuclv I forgot a comma, I have 32bit DEC that I need to convert to IEEE, njuffa solved it for me – rob ell Nov 12 '20 at 16:01

1 Answers1

2

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
njuffa
  • 23,970
  • 4
  • 78
  • 130
  • THANK YOU, this worked perfectly, I now need to deal with a 64bit version, I'm fine adapting most of it however, I'm having difficulty understanding the parts in single_precision_pdp_11 function below the "raw" array, could you explain or specify which, if any, values need to be changed and to what? – rob ell Dec 22 '20 at 16:59
  • 1
    @RobertPink I don't know off-hand what changes are needed for double precision as DEC used multiple double-precision formats. DEC single precision (F-Floating) format is very similar to IEEE-754 single precision (`binary32`). If the exponent is zero, the number is treated as zero, otherwise we re-interpret the bit-pattern as `binary32` and divide by four. This can easily be done by decrementing the exponent by 2 (the result of this conversion is exact), *unless* the result is a `binary32` subnormal, in which case we use multiplication with 0.25 (generally these cases will incur rounding error) – njuffa Dec 22 '20 at 19:52
  • 1
    @RobertPink IEEE-754 `binary32` uses exponent of `0xff` for infinities and NaNs while for the DEC format it's simply large magnitude numerical data, so we cannot use floating-point computation (e.g. multiplication) for such data, therefore we must use the exponent manipulation method. – njuffa Dec 22 '20 at 19:55