Bit shift problems in complex expressions

I compared the equation to this:

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

      

but for some reason I am getting unexpected results, so I must be doing something wrong. It started like this:

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

      

This is a completely unsatisfactory version that works. Are they not mathematically equivalent?

all values ​​are 16-bit integers. Sample dataset:

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;

      

This boils down to answer 6468. But in the first equation, my application behaves at least 3% less or more. I say this because it is a built-in application in which I cannot confirm the result of the calculation, other than to check if it is within a certain "normal" range. When I use the second equation it hits the parameters, but with my "simplified" (bit shifted) equation it doesn't, I think I should be doing the shifts wrong (or I simplified it wrong, but I triple checked it).

Any understanding is much appreciated, thanks.

+2


source to share


4 answers


From the second formula, I am assuming that you are manipulating two 16-bit values, divided by their 8-bit part a and b:

rear_wheel_speed = 0x0302
front_wheel_speed = 0x6fe2

      

and the formula you are using can be simplified to speed = (front_speed+rear_speed)*4

.



Of your values, 0x6fe2 * 4 can fit 16 bits, so this value can be estimated in 16-bit arithmetic. But the values ​​look like their parts are wrong, and I feel that the real values ​​- 0x036f and 0x02e2 (or 0x03ea and 0x026f) - these values ​​are close to each other, as you would expect from the speed of the two wheels.

It also seems that your formula is better because it does not lose precision in division operations. But keep in mind that if you are using a good compiler (this is not always true for embedded applications), it will usually convert divide / multiply to native shifts if possible

+4


source


The problem is you are getting overflow. While the equation you transformed is mathematically correct, some of your intermediate values ​​are higher than the signed 16-bit ints you store them into.

To be specific, the problematic part

( rear_wheel_speed_a + front_wheel_speed_a ) << 10

      

With your sample input, the final value is 0x1C800 - greater than an unsigned 16-bit integer!



The original equation seems to already take this into account. Some of the values ​​lose a little precision when downshifting, but this is better than an integer. So I recommend using the original equation, but you can of course replace the multiplication and separate the shifts:

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

      

One more note: your input edge_wel_speed_b is already overflowed if it shouldn't be negative.

+12


source


What you want is the average of rear_wheel_speed and front_wheel_speed, scaled to 16 bits. Since they are 16 bit values, the sum is 17 bits, so you need to shift the result to avoid overflow.

Your initial formula takes each rate by 12 bits (xxx / 16), then average, again by 12 bits, then multiplied by 128. This will take 19 bits: your original formula will overflow for larger values.

To get a 16-bit average without overflow, I suggest the following (if the values ​​are positive as you said in your comment):

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

      

This will give a 16 bit result without overflow. Each xxx_wheel_speed_h is 15 bits long.

0


source


In the original expression, the division operators throw away the least significant bits, but obviously your surrogate does not throw out any least significant bits anywhere, so one means they can't equivalent!

" rear_wheel_speed_b) / 16)

" discards the 4 low-order bits with a back_wheel_ transition before performing any operations on them, and " front_wheel_speed_b) / 16)

" discards the 4 low-order bits of front_wheel_speed. The " / 2)

" operator then discards the least significant bit of the sum.

You can only get the same result if you put something in your expression to zero out the same bits:

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

      

becomes

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

      

In other words,
yes ((((x * 256) / 16) / 2) * 128)

== ((((x << 8) >> 4) >> 1) << 7)

,
and yes, ((((x << 8) >> 4) >> 1) << 7)

== (x << 10)

,
and yes (((y / 16) / 2 ) * 128 )

== (( y >> 5 ) << 7)

,
but no (( y >> 5 ) << 7)

! = (y << 2)

!
and no (((a + b) >> 1) << 7)

! = (((a >> 1) << 7) + (((b >> 1) << 7)

!

0


source







All Articles