LINUX.ORG.RU

[delphi->cpp] 80-битный extended


0

1

Разыскивается переносимый способ конвертации дельфового 80-битного вещественного значения типа extended в плюсовый long double. Язык C++, а точнее Qt. Пофиг на потерю точности в msvc, для которого long double будет 64 битным. Важны gcc и msvc.

Нашел тут что-то, но этого мало:

MSVC win32: convert extended precision float (80-bit) to double (64-bit)

★★★★★

Разыскивается переносимый способ конвертации дельфового 80-битного вещественного значения типа extended в плюсовый long double.

А если «в лоб», при помощи сдвигов и прочих побитовых операций? Там формат не такой уж и сложный.

Deleted
()
#if __BYTE_ORDER == __LITTLE_ENDIAN

#define MAN1OFFSET  0
#define MAN2OFFSET  4
#define EXP_OFFSET  6
#define lecpy       memcpy

#elif __BYTE_ORDER == __BIG_ENDIAN

#define MAN1OFFSET  4
#define MAN2OFFSET  0
#define EXP_OFFSET  0

static inline void *lecpy(char *dst, const char *src, size_t size) {
        dst += size;
        while (size--)
                *--dst = *src++;
        return dst;
}

#endif /* __BYTE_ORDER */


void fp80le_to_fp64(void *f64, const void *f80) {
        uint32_t man1, man2;
        uint16_t exp;

        lecpy((char *) &man1, (const char *) f80, 4);
        lecpy((char *) &man2, (const char *) f80 + 4, 4);
        lecpy((char *) &exp, (const char *) f80 + 8, 2);

        man1 >>= 11;
        man1 |= (man2 & 0x7ff) << 21;
        man2 >>= 11;
        man2 &= 0x000fffff;
        man2 |= (uint32_t) (exp & 0x8000) << 16;
        exp &= 0x7fff;
        exp = (uint16_t)(exp - 0x3c00);
        man2 |= (uint32_t) (exp & 0x7ff) << 20;

        memcpy((char *) f64 + MAN1OFFSET, &man1, 4);
        memcpy((char *) f64 + MAN2OFFSET, &man2, 4);
}

void fp64_to_fp80le(void *f80, const void *f64) {
        uint32_t man1, man2;
        uint16_t exp;

        memcpy(&man1, (const char *) f64 + MAN1OFFSET, 4);
        memcpy(&man2, (const char *) f64 + MAN2OFFSET, 4);
        memcpy(&exp,  (const char *) f64 + EXP_OFFSET, 2);

        man2 = ((man2 << 11) & 0x7ffff800)
             | ((man1 >> 21) & 0x000007ff);
        if (man1 || man2)
                man2 |= 0x80000000;

        man1 <<= 11;
        if (man1 & 0x800)
                man1 |= 0x7ff;

        exp = (uint16_t)(((((exp >> 4) & 0x7ff) + 0x3c00) & 0x7fff) | (exp & 0x8000));

        lecpy((char *) f80, (const char *) &man1, 4);
        lecpy((char *) f80 + 4, (const char *) &man2, 4);
        lecpy((char *) f80 + 8, (const char *) &exp, 2);
}

BUGS: специальные значения не обрабатываются.

// ©, GPLv2

arsi ★★★★★
()
Ответ на: комментарий от Deleted

Можно и в лоб. Это нужно при записи и чтении бинарного файла.

dave ★★★★★
() автор топика
Ответ на: комментарий от arsi

Спасибо! Буду экспериментировать.

dave ★★★★★
() автор топика
Ответ на: комментарий от tailgunner

Надо подумать. Если 64-битного double будет достаточно для представления изначально 80-битного extended, то тогда вполне можно на дельфях и лазарусе написать «тупой конвертор».

dave ★★★★★
() автор топика
Ответ на: комментарий от tailgunner

Есть еще вариант поколдовать с опциями gcc.

dave ★★★★★
() автор топика
Ответ на: комментарий от nanonymous
double etodf(void *ext) {
   double result;
   __asm {
      finit
      fld tbyte ptr ext
      fstp qword ptr result
    }
   return result;
}

Как-то так. Студии под рукой нет, так что на счет синтаксиса не уверен, но общий смысл такой.

nanonymous
()
Ответ на: комментарий от ly

По-моему можно даже менять в коммерческих целях при упоминании. Довольно свободная.

dave ★★★★★
() автор топика
Ответ на: комментарий от dave

Привязан к x86 и синтаксису Intel, который используется в MSVC и GCC (если указать ключ -masm=intel).

nanonymous
()
Ответ на: комментарий от nanonymous

От дельфей идет бинарный формат файлов. А работать прога будет на линуксе.

dave ★★★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.