преобразование массива байтов в 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 ];
}
короче, нет, вы не можете преобразовать последовательность byte
s непосредственно в 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 байт, если это так для вас, а также вторая половина вашего входного массива никогда не будет видно, как сдвиг будет переполняться даже двойным типом.