在 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中的0xA
在Q6.2
中没有变化。
0xFFFF * 0xA = 0x9FFF6
,去除溢出(因此使用无符号),你在 Q6.10 中有 0xFFF6
,即 -10 / 1024
,这是你的预期结果。
最好将定点视为缩放问题,并用数字而不是位来简单明了地表达您的计算。 (Example)
AMD Q notation 中的 Q1.8 或 Q5.2 数是一个实数,乘以 28 或 22分别。
但是 C 没有 9 位或 7 位数字类型。您的 int16_t
和 uint8_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
,而不是 -1
。 0x1FF
是 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);
我正在尝试使用定点运算在 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中的0xA
在Q6.2
中没有变化。
0xFFFF * 0xA = 0x9FFF6
,去除溢出(因此使用无符号),你在 Q6.10 中有 0xFFF6
,即 -10 / 1024
,这是你的预期结果。
最好将定点视为缩放问题,并用数字而不是位来简单明了地表达您的计算。 (Example)
AMD Q notation 中的 Q1.8 或 Q5.2 数是一个实数,乘以 28 或 22分别。
但是 C 没有 9 位或 7 位数字类型。您的 int16_t
和 uint8_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
,而不是 -1
。 0x1FF
是 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);