Прочитав это: 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);
Одна мысль, которую я имел, заключалась в том, чтобы прочитать бит знака, мантисса и экспонента отдельно от данных, вычислить значение с плавающей запятой на их основе.
Ответ Себастьяна Редля верен, если вы останетесь с простым не-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:
еще нет:
Поскольку не было большой необходимости, я никогда не создавал надлежащую библиотеку для этого кода преобразования с плавающей точкой. Btw. Я использую гораздо более быстрые собственные функции byteswap, см. Https://github.com/parrot/parrot/blob/native_pbc2/include/parrot/bswap.h
Обычно вы печатаете с макс. точность для строки и прочитать эту строку. Там у вас есть только проблема, чтобы узнать свой максимум. точность.
Вы просто берете базовые байты и работаете с этим.
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));
Вы можете, конечно, оптимизировать обратное к чему-то сверхспециальному, если вы чувствуете себя настолько склонным.
Обычно вы увидите, как люди бросают неподписанное 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-разрядный целочисленный тип без знака).
ДЛЯ ВСЕХ ТЕМ:
Это идея, которую я в своем уме сделал в коде, который мне еще предстоит проверить. Может, он должен работать на машинах 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);
}