Кастинг с фиксированной точкой от 16 до 64 бит? - PullRequest
0 голосов
/ 07 ноября 2019

Макросы преобразования с фиксированной запятой для 16-битных чисел, с максимальными и минимальными значениями:

#define SCALEFACTOR_16(N) ( 1U << N )
#define Q_MAX16 (  SCALEFACTOR_16(16-1) - 1U )
#define Q_MIN16 ( -SCALEFACTOR_16(16-1)      )

Приведение минимального значения к 64 битам:

int64_t x = (int64_t)Q_MIN16;

дает:
x == 0x0000 0000 ffff 8000
, что является положительным числом, а не тем, что я ожидал.

Решение состоит в том, чтобы изменить 1U на 1UL:

#define SCALEFACTOR_16(N) ( 1UL << N )

В этом случаевывод в порядке:
x == 0xffff ffff ffff 8000

Почему первый случай не работает должным образом? Что происходит во время каста?

Ответы [ 3 ]

2 голосов
/ 07 ноября 2019

Ваша система имеет 32 бита (unsigned) int. Вся работа, которую вы выполняете, выполняется с 32-битными значениями, и вы выполняете после выполнения работы. Когда вы преобразуете от unsigned int до int64_t, оно не меняет значение (оно не интерпретирует старший бит unsigned как знаковый бит для расширения), поэтому оно заполняется нулями.

1 голос
/ 07 ноября 2019

Целочисленные константы 1U имеют тип unsigned int. Типом результата операции сдвига является тип его (повышенного) левого операнда, в этом случае unsigned int.

Выполнение сдвигов битов для типов без знака является правильным способом, но после завершения сдвига вам необходимопреобразовать обратно в предполагаемый подписанный тип. В противном случае преобразование в int64_t не «расширит знак» числа, так как компилятор видит неподписанный тип.

Обратите внимание, что -SCALEFACTOR_16 подозрительно по той же причине, но на самом деле это не так. что угодно, потому что операнд унарного минуса не подписан. При правильном преобразовании из неподписанного типа в подписанный компилятор будет обрабатывать знак автоматически, поэтому нет необходимости в том, что -.

SCALEFACTOR_16 также получит ошибку, вам нужно заключить параметр макроса в скобки.

Решение:

#define SCALEFACTOR_16(N) ( 1U << (N) )
#define Q_MAX16 ( (int16_t)(SCALEFACTOR_16(16-1) - 1U) )
#define Q_MIN16 ( (int16_t)(SCALEFACTOR_16(16-1)     ) )

Вы также можете легко сделать этот макрос довольно типичным:

#define SCALEFACTOR(N) ( 1U << (N) )
#define Q_MAX(N) ( (int##N##_t)( SCALEFACTOR(N-1) - 1U ) )
#define Q_MIN(N) ( (int##N##_t)( SCALEFACTOR(N-1)      ) )

Полный пример:

#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>

#define SCALEFACTOR(N) ( 1U << (N) )
#define Q_MAX(N) ( (int##N##_t)(  SCALEFACTOR(N-1) - 1U ) )
#define Q_MIN(N) ( (int##N##_t)( -SCALEFACTOR(N-1)      ) )

int main (void)
{
  int64_t x;
  x = Q_MIN(16);
  printf("%.16"PRIx64 " %"PRIi64 "\n", x, x);

  x = Q_MIN(32);
  printf("%.16"PRIx64 " %"PRIi64 "\n", x, x);
}
1 голос
/ 07 ноября 2019

Поскольку вы преобразуете 32-битное значение без знака (0xFFFF 8000) в 64-битное значение со знаком (0x0000 0000 FFFF 8000). Приведение значения без знака к большему типу просто добавляет 0 впереди. Приведение значения со знаком к большему типу добавляет бит знака к фронту как:

(int64_t)(int32_t)Q_MIN16; ==> 0xffff ffff ffff 8000
...