Convert extended precision float (80-bit) to double (64-bit) in MSVC

前端 未结 4 2219
暖寄归人
暖寄归人 2020-12-15 13:25

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

4条回答
  •  抹茶落季
    2020-12-15 13:59

    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;
    }
    

提交回复
热议问题