定点转换 16 位到 64 位?

Fixed-point casting 16 to 64-bits?

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

为什么第一个案例没有按预期工作?施法期间会发生什么?

您的系统是 32 位 (unsigned) int。您正在执行的所有工作都是使用 32 位值,并且您在 after 完成工作后进行转换。当您从 unsigned int 转换为 int64_t 时,它不会更改值(它不会将 unsigned 的高位解释为要扩展的符号位),因此它填零。

因为您正在将 32 位无符号值 (0xFFFF 8000) 转换为 64 位有符号值 (0x0000 0000 FFFF 8000)。将无符号值转换为更大的类型只会在前面添加 0。将有符号值转换为更大的类型会将符号位添加到前面,例如:

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

1U 整数常量的类型为 unsigned int。移位操作的结果类型是其(提升的)左操作数的类型,在本例中为 unsigned int.

尽管对无符号类型执行位移位是正确的方法,但完成移位后,您需要转换回预期的有符号类型。否则转换为 int64_t 不会 "sign extend" 编译器看到无符号类型的数字。

请注意 -SCALEFACTOR_16 出于同样的原因是可疑的,它实际上没有做任何事情,因为一元减号的操作数是无符号的。当从无符号类型正确转换为有符号类型时,编译器会自动处理符号,因此不需要 -.

SCALEFACTOR_16也有bug,宏参数需要用括号括起来

解决方案:

#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);
}