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;
如何将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;