Quick Answer:
Shifts are Multiply or Divide by a Power of Two
Shift left of M bits is equivalent to multiplication by 2^M when no overflow occurs.
Arithmetic shift right of N bit is equivalent to dividing by 2^N followed by rounding toward floor.
Integer shift left and shift right are fast machine instructions that take just one clock cycle on most modern microcontrollers.
Shifts are also much more efficient than division.
When you see shifts in generated C code for fixed-point math, the shifts usually mean multiply by 2^M or divide by 2^N.
So
( ( ( Value * 49 ) >>3 ) * 52429 ) >>15
is mathematically equivalent to
floor( ( floor( ( Value * 49 ) * 2^-3 ) * 52429 ) * 2^-15 )
which if we ignore the rounding is
Value * ( 49 * 2^-3 * 52429 * 2^-15)
or combining constants show the expected math (with input and output both uint16).
Value * 9.8
Long Answer:
Assumptions
Based on the generated code, I assume
- you've configured the gain's output data type to be uint16.
- all the fixed-point scaling Biases are zero
Ideal math
The ideal math for a gain in real-world values is
VY = VU * VG
where VG is the gain parameter value.
Replacing each of the variables with the zero-bias fixed-point scaling equation
V = Slope * Q
where Q is the stored integer value, gives
SlopeY * QY = SlopeU * QU * SlopeG * QG
solving for QY gives
QY = QU * QG * (SlopeU * SlopeG / SlopeY)
Using the known values
Based on the given information, we have
SlopeU = 1.0
SlopeY = 1.0
SlopeG = 0.1
QG = 98
Substituting gives
QY = QU * 98 * (1 * 0.1 / 1)
QY = QU * 98 * 0.1
or
QY = QU * 9.8
This is the "ideal math" expressed in terms of the stored integer values.
We expect the generated code to be equivalent to this except for rounding or overflow issues.
Why the math is done in two parts
The math for implementing the gain is effectively done in two parts
QTemp = ( Value * 49 ) >> 3
QY = ( QTemp * 52429 ) >> 15
One reason the math is broken into two parts is because the Gain block is designed to treat the gain parameter as if it could be tuned at some later point to a different stored integer value. So it starts with stored integer value 98, but it might be later tuned to another unsigned 16 bit stored integer values, such as 65001.
So the gain does NOT attempt to optimize the ideal math for
QY = QU * 98 * 0.1
Instead it tries to optimize the generated code for the ideal math
QY = QU * QG * 0.1
This could be implemented as
QY = QU * QG * 52429 * 2^-19
where 52429 * 2^-19 approximates 0.1.
But the pair of integer multiplications Q1 * QG * 52429 requires 48 bits to avoid all posible overflows over the range of tunable values for QG. For the Simulink model's specified production hardware target, the Gain block is trying to avoid going above 32 bit integers.
The operation is split into two parts with each part not exceeding 32 bits needed.
QY = ( QU * QG * 2^-4 ) * 52429 * 2^-15
or conceptually
QTemp = QU * QG * 2^-4
QY = QTemp * 52429 * 2^-15
Code Generation Optimization
Later when code is finally generated, the gain parameter is known to be constant.
Coders can exploit this information provided it doesn't change the defined math behaviors.
Coder see this math to be implemented
QTemp = QU * 98 * 2^-4
QY = QTemp * 52429 * 2^-15
Since 98 in binary has a trailing zero, this
QTemp = QU * 98 * 2^-4
can be replaced with the slightly simpler
QTemp = QU * 49 * 2^-3
Now replacing the multiplications and divisions by powers of two with shifts gives
QTemp = (QU * 49) >> 3
QY = ( QTemp * 52429 ) >> 15
This finishes the explaination of where the pieces of the generated code come from.
Recommendation
Using Slope and Bias scaling can improve accuracy. But to achieve the full accuracy and also get maximally efficient generated code, you want the output type to have scaling that matches the operation. So for gain block multiplication, you want
SlopeY = SlopeU * SlopeG
In your example, you've configured the scaling to not match
1 ~= 1 * 0.1
This causes less efficient code to be generated and removes the accuracy gains in representing 9.8 with Slope 0.1.
Either change the output of the gain to have SlopeY = 0.1.
Or use binary point scaling for the gain parameter. For example, if you use unsigned 16 bit best precision scaling
g = fi( 9.8, 0, 16 )
g =
9.800048828125000
DataTypeMode: Fixed-point: binary point scaling
Signedness: Unsigned
WordLength: 16
FractionLength: 12
Then for cases that don't overflow the output type (uint16),
the worst case rounding error is just 0.8.
And the generated code is just
QY = ( QU * 40141U ) >> 12;