在 C 中使用定点的乘法结果不正确

Incorrect multiplication result using fixed point in C

我正在尝试使用定点运算在 C 语言中实现有符号无符号乘法,但我得到的结果不正确。我无法想象如何解决这个问题。我认为位扩展存在一些问题。 这是一段代码:

int16_t audio_sample=0x1FF;      //format signed Q1.8 -> Value represented=-0.00390625
uint8_t gain=0xA;                //format unsigned Q5.2 -> Value represented = 2.5
int16_t result= (int16_t)(((int16_t)((int32_t)audio_sample * (int32_t) gain);
printf("%x",result);

printf的结果是0x13F6,这当然是0x1FF*0xA的结果,但是定点运算说正确的结果是0x3FF6,考虑适当的位扩展。 0x3FF6 在 Q6.10 格式中表示 -0.009765625=-0.00390625*2.5 .

请帮我找出错误。

提前致谢。

你应该在这里使用无符号类型。表示在您的脑海中(或注释中),而不是在代码中的数据类型中。

2的补数意味着左边的1理论上永远延续下去。例如Q1.8 中的 0x1FF 与 Q8.8 中的 0xFFFF 相同 (-1 / 256)。

如果你有一个16位整数,你不能有Q1.8,它永远是Q8.8,机器不会忽略其他位。因此,Q1.8 中的 0x1FF 应该是 Q8.8 中的 0xFFFF。 Q5.2中的0xAQ6.2中没有变化。

0xFFFF * 0xA = 0x9FFF6,去除溢出(因此使用无符号),你在 Q6.10 中有 0xFFF6,即 -10 / 1024,这是你的预期结果。

最好将定点视为缩放问题,并用数字而不是位来简单明了地表达您的计算。 (Example)

AMD Q notation 中的 Q1.8 或 Q5.2 数是一个实数,乘以 28 或 22分别。

但是 C 没有 9 位或 7 位数字类型。您的 int16_tuint8_t 变量有足够的范围来存储这些数字。但是对于算术运算,使用无符号整数,或者混合使用有符号和无符号类型是不明智的。 int 有足够的范围,避免了一些效率陷阱。

int audio_sample = -0.00390625*256;  // Q1.8
int gain = 2.5*4;  // Q5.2

数字乘以 28 和 22 的乘积的标度为 210.

int result = audio_sample * gain;  // Q6.10

要转换回实际值,请除以定标器。

printf("%lg * %lg = %lg\n",
    (double)audio_sample/256,
    (double)gain/4,
    (double)result/1024);

Please help me find my mistake.

错误在于将 0x1FF 分配给 audio_sample,而不是 -10x1FF 是 9 位二进制补码值 -1 的无符号截断。但是 audio_sample 更宽,需要更多前导 1 位。通过将 -0.00390625*256 分配给 audio_sample 来表达您的意图会更清楚、更安全。

the fixed-point arithmetics said that the correct results would be 0x3FF6, considering the proper bit-extension

0x3FF6 是正确二进制补码答案的无符号 14 位截断。但结果需要 16 位,因此您可能正在寻找值,0xFFF6.

printf("unsigned Q6.10: 0x%x\n", (unsigned)result & 0xFFFF);