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;
}
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
User contributions licensed under CC BY-SA 3.0