Я получаю некоторые значения из аппаратных регистров, где значения хранятся в 16-разрядных целых числах без знака, но эти значения на самом деле подписаны. Зная, что последний бит является знаковым битом, коллега сделал следующий фрагмент кода, чтобы преобразовать их в значения дополнения 2:
/* Take 15 bits of the data (last bit is the sign) */
#define DATAMASK 0x7FFF
/* Sign is bit 15 (starting from zero) with the 15 bit data */
#define SIGNMASK 0x8000
#define SIGNBIT 15int16_t calc2sComplement(uint16_t data)
{
int16_t temp, sign;
int16_t signData;
sign = (int16_t)((data & SIGNMASK) >> SIGNBIT);
if (sign)
{
temp = (~data) & DATAMASK;
signData = (short)(temp * -1);
}
else
{
temp = (data & DATAMASK);
signData = temp;
}
return(signData);
}
Насколько я знаю, целые типы без знака и целые числа со знаком отличаются только по их типу и значению последнего бита; так что приведение типа следующее должно также работать:
int16_t calc2sComplement(uint16_t data)
{
return(static_cast<int16_t>(data));
}
и когда необходимо передать значения аппаратным средствам, обратная операция проста, в отличие от расчета. Преимущество первого решения заключается в отсутствии инструментов; так как он может измениться рано или поздно (gcc 4.4.7 и т. д. C ++ 03), я бы предпочел не делать этого, но не будет регрессии при компиляции спустя годы. Преимущество последнего в том, что он более читабелен, близок к стандарту и позволяет избежать ненужных операций.
Что было бы лучше всего в моем случае, чтобы быть уверенным, чтобы сохранить то же поведение, если компилируется снова после изменения цепочки инструментов (даже стандартные типы переопределены где-то в инструментальной цепочке, и я не очень-то понимаю)
Если вы оставите первое решение, как его улучшить и / или закодировать обратное преобразование (имейте в виду, что данные могут быть указателем на буфер данных)?
В конце давайте ответим себе. Таким образом, лучший способ преобразовать значения в или из дополнения до двух и предотвратить непредвиденное поведение — выполнить преобразование дополнения до двух следующим образом:
int16_t calc2sComplement(uint16_t data)
{
return(static_cast<int16_t>(data));
}
и сделать обратную операцию:
uint16_t inv2sComplement(int16_t data)
{
return(static_cast<uint16_t>(data));
}
Этот метод оказался полностью безопасным (если типы примитивов не переопределены где-то в цепочке инструментов — что считается плохой практикой, но на самом деле это был мой случай, а следовательно, и мой вопрос в первую очередь), опираясь на определение построенного примитива. в типах.
Других решений пока нет …