Какой самый правильный способ изменить порядковый номер числа с плавающей точкой

0

Прочитав это: http://commandcenter.blogspot.fi/2012/04/byte-order-fallacy.html

Метод в статье таков:

Читайте от большого эндианта:

int i = (data[3]<<0) | (data[2]<<8) | (data[1]<<16) | (data[0]<<24);

Читайте от маленького эндианта:

int i = (data[0]<<0) | (data[1]<<8) | (data[2]<<16) | (data[3]<<24);

Есть ли способ преобразовать эту идеологию в числа с плавающей запятой?

Таким образом, есть ли способ избежать if(swap_needed) swap(data);

Одна мысль, которую я имел, заключалась в том, чтобы прочитать бит знака, мантисса и экспонента отдельно от данных, вычислить значение с плавающей запятой на их основе.

  • 2
    Если вы читаете и пишете файлы, вы можете выбрать один из двух порядковых номеров и всегда читать / писать в этом макете независимо от типа машины.
  • 2
    Чтобы ответить на ваш вопрос относительно чисел с плавающей запятой: Нет. Числа с плавающей запятой не имеют «порядковый номер» так же, как целые числа.
Показать ещё 6 комментариев
Теги:
endianness

4 ответа

3
Лучший ответ

Ответ Себастьяна Редля верен, если вы останетесь с простым не-Intel IEEE-754 поплавком или двойным, но он не справится с особенным представлением Intel двойного и длинного двойного и всех других специальных идей для их длинных двойных форматов. Только в очень немногих архитектурах используются стандартные форматы с плавающей запятой IEEE-754. Даже самые простые мипы, которые могут использовать BE/LE по своему усмотрению, имеют специальный двойной формат MIPS64 длиной 16 байт.

Таким образом, нет правильного и простого способа быстрой записи байтов для float. Однако я написал код, чтобы читать поплавки из разных архитектур в текущую архитектуру, что является задачей геркулеса. https://github.com/parrot/parrot/blob/native_pbc2/src/packfile/pf_items.c#L553 Примечание. Спецификацией intel является дополнительный бит нормализации (самый высокий бит 63 мантиссы), отмеченный я в https://github.com/parrot/parrot/blob/native_pbc2/src/packfile/pf_items.c#L605

Т.е. я конвертирую между этими, BE и LE:

  • Floattype 0 = IEEE-754 8 байтов double (binary64)
  • Floattype 1 = Intel 80-бит long double хранится в 12 байт (i386) или выровнен по 16 байт (x86_64/ia64)
  • Floattype 2 = IEEE-754 128-битная четность, хранящаяся в 16 байт, Quad-float Sparc64 или __float128, gcc с 4.3 (binary128)
  • Floattype 3 = IEEE-754 4 байта float (binary32)
  • Floattype 4 = PowerPC 16 байтов double-double (-mlong-double-128)

еще нет:

  • Floattype 5 = IEEE-754 2 байта с половинной точностью float (двоичный 16)
  • Floattype 6 = MIPS64 16-байтовый длинный двойной
  • Floattype 7 = AIX 16-байтовый длинный двойной
  • CRAY и больше сумасшествия

Поскольку не было большой необходимости, я никогда не создавал надлежащую библиотеку для этого кода преобразования с плавающей точкой. Btw. Я использую гораздо более быстрые собственные функции byteswap, см. Https://github.com/parrot/parrot/blob/native_pbc2/include/parrot/bswap.h

Обычно вы печатаете с макс. точность для строки и прочитать эту строку. Там у вас есть только проблема, чтобы узнать свой максимум. точность.

  • 1
    Я посмотрел на то, как работает код, он выглядит очень похоже на то, что я делал, извлекая различные части чисел с плавающей запятой и пересчитывая число из них. Чего я не понимаю, так это того, почему меня понизили, вы замечаете что-то ужасно неправильное в моей основной идее преобразования? Я понимаю, что он не обрабатывает все форматы и все случаи, но основная идея есть.
1

Вы просто берете базовые байты и работаете с этим.

unsigned char underlying[sizeof(float)];

// Writing
std::memcpy(underlying, &my_float, sizeof(float));
if (platform_endian != target_endian)
  std::reverse(std::begin(underlying), std::end(underlying));
write(underlying, sizeof(float));

// Reading
read(underlying, sizeof(float));
if (platform_endian != target_endian)
  std::reverse(std::begin(underlying), std::end(underlying));
std::memcpy(&my_float, underlying, sizeof(float));

Вы можете, конечно, оптимизировать обратное к чему-то сверхспециальному, если вы чувствуете себя настолько склонным.

0

Обычно вы увидите, как люди бросают неподписанное 64-битное целое число, а затем вызывают классические функции BSD для преобразования в/из байта сети. Я когда-то работал над проектом, где я получил double по сети с Java-машины, поэтому я знал, что их отправили big-endian, и прочитал их на компьютере Intel в C++. Я просто читаю данные как char[8], называемый std::reverse, и передаю результат в double:

double read_double()
{
    char buffer[8];
    // read from network into buffer;
    std::reverse(std::begin(buffer), std::end(buffer), std::begin(buffer));
    return *static_cast<double*>(static_cast<void*>(buffer));
}

Сегодня я буду делать все по-другому. Во-первых, код с битрейтом, который вы отправили, не так уж и сложно выполнить. С другой стороны, я согласен с @NeilKirk и статьей, с которой вы связались: код для чтения/записи с определенной контентой идентичен независимо от фактической конкретизации на машине, поэтому просто напишите код, который будет читать шрифты big-endian/little-endian данных, используя код из статьи, к которой вы привязались (просто добавьте ее в double после того, как вы прочитали и обработали байты как 64-разрядный целочисленный тип без знака).

-3

РЕДАКТИРОВАТЬ

ДЛЯ ВСЕХ ТЕМ:

Ответ, который я обозначил как правильный, имеет в нем одну и ту же основную идею. Если вы уменьшите этот показатель, уменьшите также "правильную".

Это идея, которую я в своем уме сделал в коде, который мне еще предстоит проверить. Может, он должен работать на машинах LE и BE? Я проведу его позже, когда у меня будет несколько данных теста.

#include <math.h>
#include <ace/Basic_Types.h>

double readDoubleBe(unsigned char * p_data)
{
    ACE_UINT64 doubleData = p_data[0] << 56;
    doubleData += p_data[1] << 48;
    doubleData += p_data[2] << 40;
    doubleData += p_data[3] << 32;
    doubleData += p_data[4] << 24;
    doubleData += p_data[5] << 16;
    doubleData += p_data[6] << 8;
    doubleData += p_data[7];

    ACE_INT64 fractal = doubleData & 0x000fffffffffffff; // should be 52 ones
    ACE_UINT16 mantissa = (doubleData >> 52) & 0x0eff; // 11 bits, leave the sign bit out

    if(doubleData >> 63) != 0)
    {
        fractal = -fractal;
    }

    return (double(fractal) / 0x0010000000000000 + 1.0) * pow(2, mantissa - 1023);
}

Ещё вопросы

Сообщество Overcoder
Наверх
Меню