C++ как объединить два подписанных 8-битных числа в 16-битный короткий? Необъяснимые результаты
Мне нужно объединить два подписанных 8-битных значения _int8 в подписанное короткое (16-битное) значение. Важно, чтобы знак не потерялся.
мой код:
unsigned short lsb = -13;
unsigned short msb = 1;
short combined = (msb << 8 )| lsb;
результат, который я получаю, -13. Однако я ожидаю, что это будет 499.
для следующих примеров, я получаю правильные результаты с тем же кодом:
msb = -1; lsb = -6; combined = -6;
msb = 1; lsb = 89; combined = 345;
msb = -1; lsb = 13; комбинированный = -243;
однако msb = 1; lsb = -84; combined = -84; где я ожидал бы 428.
Кажется, что если lsb отрицательный, а msb положительный, что-то идет не так! Что не так с моим кодом? Как компьютер добирается до этих неожиданных результатов (Win7, 64 бит и VS2008 c++)? Большое спасибо за любую помощь!
7 ответов
ваш lsb в этом случае содержит 0xfff3. Когда вы или он с 1
попробовать short combined = (msb << 8 ) | (lsb & 0xff);
или с помощью союза:
#include <iostream>
union Combine
{
short target;
char dest[ sizeof( short ) ];
};
int main()
{
Combine cc;
cc.dest[0] = -13, cc.dest[1] = 1;
std::cout << cc.target << std::endl;
}
возможно, что lsb
автоматически расширяется до 16 бит. Я замечаю, что у вас есть проблема только тогда, когда она отрицательная, а msb положительная, и это то, что вы ожидаете, учитывая, как вы используете оператор or. Хотя, ты явно делаешь здесь что-то очень странное. Что ты на самом деле пытаешься здесь сделать?
raisonanse c complier для STM8 (и, возможно, многих других компиляторов) генерирует уродливый код для классического кода C при записи 16-битных переменных в 8-битные аппаратные регистры. Примечание-STM8 является big-endian, для мало-endian CPUs код должен быть слегка изменен. Порядок байтов чтения / записи также важен.
Итак, стандартная часть кода C:
unsigned int ch1Sum;
...
TIM5_CCR1H = ch1Sum >> 8;
TIM5_CCR1L = ch1Sum;
компилируется в:
;TIM5_CCR1H = ch1Sum >> 8;
LDW X,ch1Sum
CLR A
RRWA X,A
LD A,XL
LD TIM5_CCR1,A
;TIM5_CCR1L = ch1Sum;
MOV TIM5_CCR1+1,ch1Sum+1
слишком долго, слишком медленно.
мой версия:
unsigned int ch1Sum;
...
TIM5_CCR1H = ((u8*)&ch1Sum)[0];
TIM5_CCR1L = ch1Sum;
это скомпилировано в адекватные два хода
;TIM5_CCR1H = ((u8*)&ch1Sum)[0];
MOV TIM5_CCR1,ch1Sum
;TIM5_CCR1L = ch1Sum;
MOV TIM5_CCR1+1,ch1Sum+1
противоположном направлении:
unsigned int uSonicRange;
...
((unsigned char *)&uSonicRange)[0] = TIM1_CCR2H;
((unsigned char *)&uSonicRange)[1] = TIM1_CCR2L;
вместо
unsigned int uSonicRange;
...
uSonicRange = TIM1_CCR2H << 8;
uSonicRange |= TIM1_CCR2L;
некоторые вещи, которые вы должны знать о типах данных (un)подпись короче и char:
char является 8-битным значением, это то, что вы где ищете для lsb и msb. короче - 16 бит в длину.
вы также не должны хранить подпись значения без подписи те execpt вы знаете, что вы делаете.
вы можно взглянуть на дополнения two. Он описывает представление отрицательных значений (для целых чисел, а не для значений с плавающей точкой) в C/C++ и многих других языков программирования.
существует несколько версий создания дополнения ваших собственных двух:
int a;
// setting a
a = -a; // Clean version. Easier to understand and read. Use this one.
a = (~a)+1; // The arithmetical version. Does the same, but takes more steps.
// Don't use the last one unless you need it!
// It can be 'optimized away' by the compiler.
stdint.h (с inttypes.h) больше для того, чтобы иметь точные длины для вашей переменной. Если вам действительно нужна переменная с определенной длиной байта вы должны использовать это (здесь вам это нужно).
вы должны everythime использовать типы данных, которые соответствуют вашим потребностям лучше всего. Поэтому ваш код должен выглядеть так:
signed char lsb; // signed 8-bit value
signed char msb; // signed 8-bit value
signed short combined = msb << 8 | (lsb & 0xFF); // signed 16-bit value
или такой:
#include <stdint.h>
int8_t lsb; // signed 8-bit value
int8_t msb; // signed 8-bit value
int_16_t combined = msb << 8 | (lsb & 0xFF); // signed 16-bit value
для последнего компилятор будет использовать подписанные 8/16-битные значения каждый раз, независимо от длины int есть на вашей платформе. Википедия получил хорошее объяснение int8_t и int16_t типы данных (и все остальные типы данных).
btw:cppreference.com полезно для поиска ANSI C стандарты и другие вещи, которые стоит знать о C/с++.
вы написали, что вам нужно объединить два 8-разрядных значений. Почему вы используете unsigned short
потом?
As Dan
уже сказал, lsb
автоматически расширяется до 16 бит. Попробуйте следующий код:
uint8_t lsb = -13;
uint8_t msb = 1;
int16_t combined = (msb << 8) | lsb;
это дает вам ожидаемый результат: 499
.
Если это то, что вы хотите:
msb: 1, lsb: -13, combined: 499
msb: -6, lsb: -1, combined: -1281
msb: 1, lsb: 89, combined: 345
msb: -1, lsb: 13, combined: -243
msb: 1, lsb: -84, combined: 428
используйте этот:
short combine(unsigned char msb, unsigned char lsb) {
return (msb<<8u)|lsb;
}
Я не понимаю, почему вы хотите, чтобы msb -6 и lsb -1 генерировали -6.