复杂表达式中的位移问题

时间:2009-09-08 07:09:07

标签: c++ math embedded types bit-shift

我已经提炼出一个等式:

speed = ( ( rear_wheel_speed_a + front_wheel_speed_a ) << 10 ) +
        ( ( rear_wheel_speed_b + front_wheel_speed_b ) << 2 );

但出于某种原因我得到了意想不到的结果,所以我一定做错了。这开始是这样的:

speed = ((((rear_wheel_speed_a * 256 + rear_wheel_speed_b) / 16) +
        ((front_wheel_speed_a * 256 + front_wheel_speed_b) / 16)) / 2) * 128;

这是完全不起作用的版本。它们不是数学上的等价物吗?

所有值都是16位整数。示例数据集是:

rear_wheel_speed_a = 0x03;
rear_wheel_speed_b = 0x6F; //<-- I originally swapped
front_wheel_speed_a = 0x02; //<-- these two values. Sorry!
front_wheel_speed_b = 0xE2;

归结为6468的答案。但在第一个等式中,我的应用程序表现得好像它至少小3%或更大。我这样说是因为这是一个嵌入式应用程序,我无法确认计算结果,只能测试它是否在“正常”的某个范围内。当我使用第二个等式时,它属于参数,但是我的“简化”(位移)方程式并不是这样,我认为我必须做错误的转换(或者我简化了错误,但我对其进行了三次检查)。

非常感谢您,谢谢。

4 个答案:

答案 0 :(得分:12)

问题是你得到了溢出。虽然您转换的等式在数学上是正确的,但是您的某些临时值高于您存储它们的带符号的16位整数。

具体而言,有问题的部分是

( rear_wheel_speed_a + front_wheel_speed_a ) << 10

使用示例输入,结果值为0x1C800 - 大于无符号的16位整数!

原始等式似乎已经考虑到了这一点。降档时,有些值会略微失去精度,但这比整数溢出要好。所以我建议使用原始等式,但你可以用乘法替换乘法和除法,当然:

((((rear_wheel_speed_a << 8) + rear_wheel_speed_b) >> 4) + (((front_wheel_speed_a << 8) + front_wheel_speed_b) >> 4)) << 6;

另一个注意事项:您的输入front_wheel_speed_b已经溢出,除非它应该是负数。

答案 1 :(得分:4)

从第二个公式中,我假设您将2个16位值分成8位a和b:

rear_wheel_speed = 0x0302
front_wheel_speed = 0x6fe2

和您使用的公式可以简化为speed = (front_speed+rear_speed)*4

根据您的值,0x6fe2 * 4只能适合16位,因此可以用16位算术计算该值。但是这些值看起来像是它们的部分排列错了,我感觉实际值是0x036f和0x02e2(或0x03ea和0x026f) - 这些值彼此接近,应该从两个车轮的速度预期。

此外,您的公式似乎更好,因为它不会导致除法运算的精度损失。但请记住,如果您使用的是良好的编译器(对于嵌入式应用程序并不总是如此),它通常会在可能的情况下将divide / multiply转换为自身的移位

答案 2 :(得分:0)

你想要的是rear_wheel_speed和front_wheel_speed的平均值,缩放到16位。由于它们是16位值,因此总和为17位,因此您必须移位结果以避免溢出。

您的初始公式将每个速度设置为12位(xxx / 16),然后是12位的平均值,然后乘以128.这将需要19位:初始公式将溢出更大的值。

为了得到16位没有溢出的平均值,我建议如下(假设你的评论中的值为正值):

rear_wheel_speed_h = (rear_wheel_speed_a << 7) | (rear_wheel_speed_b >> 1)
front_wheel_speed_h = (front_wheel_speed_a << 7) | (front_wheel_speed_b >> 1)
speed = rear_wheel_speed_h + front_wheel_speed_h

这将在16位上产生没有溢出的结果。每个xxx_wheel_speed_h都是15位。

答案 3 :(得分:0)

在原始表达式中,除法运算符丢弃了低位,但很明显,你的替代品并没有丢弃任何低位的位数,所以仅此意味着它们不能等效!

rear_wheel_speed_b) / 16)”在对它们执行任何操作之前丢弃rear_wheel_speed的4个低位,并且“front_wheel_speed_b) / 16)”丢弃front_wheel_speed的4个低位。然后“/ 2)”运算符丢弃总和的低位。

如果你在表达式中添加了一些东西,那么你只能得到完全相同的结果:

speed = ((((rear_wheel_speed_a * 256 + rear_wheel_speed_b) / 16) +
        ((front_wheel_speed_a * 256 + front_wheel_speed_b) / 16)) / 2) * 128;

变为

speed = ( ( rear_wheel_speed_a + front_wheel_speed_a ) << 10 ) +
        ( ( ( ( rear_wheel_speed_b & ~0x0F ) + ( front_wheel_speed_b & ~0x0F ) ) & ~1) << 2 );

换句话说,
((((x * 256) / 16) / 2) * 128) == ((((x << 8) >> 4) >> 1) << 7)
是的,((((x << 8) >> 4) >> 1) << 7) == (x << 10)
是的(((y / 16) / 2 ) * 128 ) == (( y >> 5 ) << 7)
但是没有 (( y >> 5 ) << 7)!= (y << 2)
没有 (((a + b) >> 1) << 7)!= (((a >> 1) << 7) + (((b >> 1) << 7)