преобразование массива байтов в double-c

Я пытаюсь получить числовое (двойное) значение из байтового массива из 16 элементов следующим образом:

unsigned char input[16];
double output;
...
double a  = input[0];
distance = a;
for (i=1;i<16;i++){
    a = input[i] << 8*i;
    output += a;
}

но это не работает. Кажется, что временная переменная, содержащая результат левого сдвига, может хранить только 32 бита, потому что после 4 операций сдвига 8 бит она переполняется.

Я знаю, что могу использовать что-то вроде

a = input[i] * pow(2,8*i);

но, для любопытства, мне было интересно, есть ли какое-либо решение этой проблемы, используя сдвиг оператор...

7 ответов


Edit: это не будет работать (см. комментарий) без чего-то вроде __int128.

a = input[i] << 8*i;

выражение input[i] превращается в int (6.3.1.1), который 32bit на вашей машине. Чтобы преодолеть эту проблему, левый операнд должен быть 64bit, как в

a = (1L * input[i]) << 8*i;

или

a = (long long unsigned) input[i] << 8*i;

и помните о байт


проблема здесь в том, что 32-битные переменные не могут быть сдвинуты более чем в 4*8 раз, т. е. ваш код работает только для 4 символов.

что вы можете сделать, это найти первый значимый символ и использовать закон Хорнера: anxn + an-1n-1 + ... = ((...(anx + an-1 ).x + an-2 ) . икс. + .. ) + a0 следующим образом:

char coefficients[16] = { 0, 0, ..., 14, 15 };
int exponent=15;
double result = 0.;

for(int exponent = 15; exp >= 0; --exp ) {
   result *= 256.; // instead of <<8.
   result += coefficients[ exponent ];
}

короче, нет, вы не можете преобразовать последовательность bytes непосредственно в double по бит-сдвигу, как показано в примере кода.

byte, целочисленный тип и double, тип с плавающей запятой (т. е. не целочисленный тип) не являются битовыми совместимыми (т. е. вы не можете просто переключаться на значения группы байтов в тип с плавающей запятой и ожидать эквивалентного результата.)

1) предполагая, что массив байтов является буфером памяти, ссылающимся на целое значение, вы должны быть возможность конвертировать ваш byte массив в 128-битное целое число с помощью битового сдвига, а затем преобразовать это результирующее целое число в double. Не забывайте, что прямой-проблемы могут вступить в игру в зависимости от архитектуры процессора.

2) предполагая, что массив байтов является буфером памяти, который содержит 128-битное двойное значение, и предполагая, что нет никаких проблем с endian, вы должны иметь возможность memcpy значение из массива байтов в длинное двойное значение

union doubleOrByte {
    BYTE buffer[16];
    long double val;
} dOrb;
dOrb.val = 3.14159267;
long double newval = 0.0;

memcpy((void*)&newval, (void*)dOrb.buffer, sizeof(dOrb.buffer));

почему бы просто не привести массив к двойному указателю?

unsigned char input[16];

double* pd = (double*)input;

for (int i=0; i<sizeof(input)/sizeof(double); ++i)
    cout << pd[i];

Если вам нужно исправить endian-ness, отмените массив char с помощью STL reverse () перед приведением к двойному массиву.



вы пытаетесь преобразовать строковое представление числа в вещественное число? В этом случае c-standard atof-ваш лучший друг.


хорошо основанный от приоритета оператора правая сторона

a = input[i] << 8*i;

получает оценку, прежде чем он преобразуется в double, поэтому вы сдвигаете input[i] на 8*I бит, который сохраняет свой результат в 32-битной временной переменной и, таким образом, переполняется. Вы можете попробовать следующее:

a = (long long unsigned int)input[i] << 8*i;

Edit: не уверен, что размер двойника в вашей системе, но на моем это 8 байт, если это так для вас, а также вторая половина вашего входного массива никогда не будет видно, как сдвиг будет переполняться даже двойным типом.