Функция отображения, определенная OP, в настоящее время выглядит следующим образом:
void display32Q16 (int32_t num)
{
int16_t integer = num >> 16;
uint64_t fraction = ((0x0000FFFF & num) * 10000) >> 16;
printf("Number = %d.%" PRIu64 "\n", integer, fraction);
}
Однако, это не отображает отрицательные дроби должным образом, потому что представление дополнения 2 отрицательного числа имеет большинство инвертированных битов по сравнению сположительное число такой же величины. Он также печатает только 4 десятичных знака для дробной части, что не так точно, как могло бы быть.
Например, -3276.1 представлено как значение int32_t
-214702489 (-3276.1 * 65536 сдробная часть отбрасывается). Это имеет тот же битовый шаблон, что и 0xF333E667. Функция отображения определяет целочисленную часть, которая должна быть напечатана, путем арифметического сдвига значения влево на 16 битов, представленного битовой комбинацией 0xFFFFF333, и усечения до 16 битов, представленных битовой комбинацией 0xF333. Как целое число со знаком, эта битовая комбинация соответствует -3277. Дробная часть, которая должна быть напечатана, определяется путем взятия младших 16 битов, представленных битовой комбинацией 0xE667, соответствующей десятичному числу 58983, умножения на 10000, что приводит к 589830000, и сдвига вправо на 16 бит (деления на 65536), что приводит к9000. Поэтому значение печатается как -3277.9000.
Вот функция, которая правильно отображает число, используя 5 дробных цифр:
void display32Q16 (int32_t num)
{
int32_t integer = num >> 16;
uint64_t fraction = num & 0xFFFF;
const char *xtrasign = "";
if (integer < 0 && fraction != 0)
{
integer++;
fraction = 65536 - fraction;
}
fraction = (fraction * 100000) >> 16;
if (num < 0 && integer == 0)
{
/* special case for number between -1 and 0 */
xtrasign = "-";
}
printf("Number = %s%" PRIi32 ".%05" PRIu64 "\n", xtrasign, integer, fraction);
}
Обратите внимание, что исходное число, полученное из -32761/10 будет отображаться как -3276.09999, потому что дробная часть 0.1 не может быть представлена точно в двоичном виде.
Может быть предпочтительнее округлить до 4 десятичных знаков следующим образом, что также устраняет необходимость в 64-битных числах.
void display32Q16 (int32_t num)
{
int32_t integer = num >> 16;
uint32_t fraction = num & 0xFFFF;
const char *xtrasign = "";
if (integer < 0 && fraction != 0)
{
integer++;
fraction = 65536 - fraction;
}
fraction = ((fraction * 10000) + (1 << 15)) >> 16;
if (fraction >= 10000)
{
/* deal with fraction rounding overflow */
if (num < 0)
integer--;
else
integer++;
fraction -= 10000;
}
if (num < 0 && integer == 0)
{
/* special case for number between -1 and 0 */
xtrasign = "-";
}
printf("Number = %s%" PRIi32 ".%04" PRIu32 "\n", xtrasign, integer, fraction);
}