What is the most portable and \"right\" way to do conversion from extended precision float (80-bit value, also known as long double
in some compilers) to double
I've just written this one. It constructs an IEEE double number from IEEE extended precision number using bit operations. It takes the 10 byte extended precision number in little endian format:
typedef unsigned long long uint64;
double makeDoubleFromExtended(const unsigned char x[10])
{
int exponent = (((x[9] << 8) | x[8]) & 0x7FFF);
uint64 mantissa =
((uint64)x[7] << 56) | ((uint64)x[6] << 48) | ((uint64)x[5] << 40) | ((uint64)x[4] << 32) |
((uint64)x[3] << 24) | ((uint64)x[2] << 16) | ((uint64)x[1] << 8) | (uint64)x[0];
unsigned char d[8] = {0};
double result;
d[7] = x[9] & 0x80; /* Set sign. */
if ((exponent == 0x7FFF) || (exponent == 0))
{
/* Infinite, NaN or denormal */
if (exponent == 0x7FFF)
{
/* Infinite or NaN */
d[7] |= 0x7F;
d[6] = 0xF0;
}
else
{
/* Otherwise it's denormal. It cannot be represented as double. Translate as singed zero. */
memcpy(&result, d, 8);
return result;
}
}
else
{
/* Normal number. */
exponent = exponent - 0x3FFF + 0x03FF; /*< exponent for double precision. */
if (exponent <= -52) /*< Too small to represent. Translate as (signed) zero. */
{
memcpy(&result, d, 8);
return result;
}
else if (exponent < 0)
{
/* Denormal, exponent bits are already zero here. */
}
else if (exponent >= 0x7FF) /*< Too large to represent. Translate as infinite. */
{
d[7] |= 0x7F;
d[6] = 0xF0;
memset(d, 0x00, 6);
memcpy(&result, d, 8);
return result;
}
else
{
/* Representable number */
d[7] |= (exponent & 0x7F0) >> 4;
d[6] |= (exponent & 0xF) << 4;
}
}
/* Translate mantissa. */
mantissa >>= 11;
if (exponent < 0)
{
/* Denormal, further shifting is required here. */
mantissa >>= (-exponent + 1);
}
d[0] = mantissa & 0xFF;
d[1] = (mantissa >> 8) & 0xFF;
d[2] = (mantissa >> 16) & 0xFF;
d[3] = (mantissa >> 24) & 0xFF;
d[4] = (mantissa >> 32) & 0xFF;
d[5] = (mantissa >> 40) & 0xFF;
d[6] |= (mantissa >> 48) & 0x0F;
memcpy(&result, d, 8);
printf("Result: 0x%016llx", *(uint64*)(&result) );
return result;
}