64位和32位定点数之间的转换

Conversion between 64-bit and 32-bit fixed-point numbers

如何将Q33.31格式的数据转换成Q2.30格式的数据?我知道如果输入和输出的位大小相同,我们需要使用移位运算符。但是如何计算它们是否不同大小?

这个怎么样:

uint32_t convert(uint64_t x)
{
    uint32_t hi = (uint32_t)(x >> 32);
    uint32_t lo = (uint32_t)(x);
    if (hi >= (1 << 2) || lo >= (1 << 30))
        // handle input-too-large-or-too-accurate error and exit
    return (hi << 30) | lo;
}

除了处理 if 语句中的错误输入(如果您不关心可能的信息丢失),您可以简单地 return (hi << 30) | ((lo << 2) >> 2);.

在对@goodvibration 的回答的评论中,您声明要添加两个 Q1.31 数字。鉴于此,您知道您的结果可以表示为 Q2.31,因此要将您的 Q2.31 数字转换为 Q2.30,您只需将结果右移一位:

uint32_t convert_q231_q230(uint64_t x)
  {
  return (uint32_t) (x >> 1);
  }

这里的关键只是将小数点移到正确的位置。举个简单的例子从Q9.7格式到Q2.6这样

in  9 8 7 6 5 4 3 2 1.1 2 3 4 5 6 7
out                 2 1.1 2 3 4 5 6

如您所见,输出的小数点位置在输入的右边1,所以我们需要右移以将其放在正确的位置。您也可以这样想:输出的小数部分少了 1 位,因此我们将右移 1 位以将其从 7 位截断为 6 位。当您对较窄的类型进行赋值时,整数部分的 7 个高位将在 C 中自动截断。这相当于

uint8_t out = in >> 1;

类似于从 Q33.31 转换为 Q2.30,您将执行相同的操作:q2_30 = q33_31 >> 1

但是现在要获得更正确的结果,您需要执行舍入步骤。舍入方法有很多,但最简单的方法是 舍入到最近的 ,方法是检查值是高于还是低于 0.5。就像在十进制中我们检查第一个被截断的数字以查看它是否 >= 5 一样,在二进制中我们检查移出的最后一位并将其添加回结果,如下所示

uint32_t q2_30 = (q33_31 >> 1) + (q33_31 & 1)

编辑

当您只需要两个 Q1.31 位数的总和时,绝对不需要截断来执行此操作。用上面的方法把它们转成Q2.30就可以了,后面加上再四舍五入

uint32_t A2_30 = A1_31 >> 1; // types must be unsigned so that the shifts are logical
uint32_t B2_30 = B1_31 >> 1; // instead of arithmetic

// if only one of the values is 1 then their sum is 0.5 ULP which will be rounded to 1
uint32_t carry = (A1_31 & 1) | (B1_31 & 1); // if both of them are 1 then sum = 1 ULP

Q2_30 sum = A2_30 + B2_30 + carry;