Manual FixedPoint Conversion Best Practices
FixedPoint Designer™ software helps you design and convert your algorithms to fixed point. Whether you are simply designing fixedpoint algorithms in MATLAB^{®} or using FixedPoint Designer in conjunction with MathWorks^{®} code generation products, these best practices help you get from generic MATLAB code to an efficient fixedpoint implementation. These best practices are also covered in this webinar: Manual FixedPoint Conversion Best Practices Webinar
Create a Test File
A best practice for structuring your code is to separate your core algorithm from other code that you use to test and verify the results. Create a test file to call your original MATLAB algorithm and fixedpoint versions of the algorithm. For example, as shown in the following table, you might set up some input data to feed into your algorithm, and then, after you process that data, create some plots to verify the results. Since you need to convert only the algorithmic portion to fixedpoint, it is more efficient to structure your code so that you have a test file, in which you create your inputs, call your algorithm, and plot the results, and one (or more) algorithmic files, in which you do the core processing.
Original code  Best Practice  Modified code 

% TEST INPUT x = randn(100,1); % ALGORITHM y = zeros(size(x)); y(1) = x(1); for n=2:length(x) y(n)=y(n1) + x(n); end % VERIFY RESULTS yExpected=cumsum(x); plot(yyExpected) title('Error')  Issue Generation of test input and verification of results are intermingled with the algorithm code. Fix Create a test file that is separate from your algorithm. Put the algorithm in its own function.  Test file % TEST INPUT x = randn(100,1); % ALGORITHM y = cumulative_sum(x); % VERIFY RESULTS yExpected = cumsum(x); plot(yyExpected) title('Error') Algorithm in its own function function y = cumulative_sum(x) y = zeros(size(x)); y(1) = x(1); for n=2:length(x) y(n) = y(n1) + x(n); end end 
You can use the test file to:
Verify that your floatingpoint algorithm behaves as you expect before you convert it to fixed point. The floatingpoint algorithm behavior is the baseline against which you compare the behavior of the fixedpoint versions of your algorithm.
Propose fixedpoint data types.
Compare the behavior of the fixedpoint versions of your algorithm to the floatingpoint baseline.
Your test files should exercise the algorithm over its full operating range so that the simulation ranges are accurate. For example, for a filter, realistic inputs are impulses, sums of sinusoids, and chirp signals. With these inputs, using linear theory, you can verify that the outputs are correct. Signals that produce maximum output are useful for verifying that your system does not overflow. The quality of the proposed fixedpoint data types depends on how well the test files cover the operating range of the algorithm with the accuracy that you want.
Prepare Your Algorithm for Code Acceleration or Code Generation
Using FixedPoint Designer, you can:
Instrument your code and provide data type proposals to help you convert your algorithm to fixed point, using the following functions:
buildInstrumentedMex
, which generates compiled C code that includes logging instrumentation.showInstrumentationResults
, which shows the results logged by the instrumented, compiled C code.clearInstrumentationResults
, which clears the logged instrumentation results from memory.
Accelerate your fixedpoint algorithms by creating a MEX file using the
fiaccel
function.
Any MATLAB algorithms that you want to instrument using
buildInstrumentedMex
and any fixedpoint algorithms that you want to
accelerate using fiaccel
must comply with code generation requirements
and rules. To view the subset of the MATLAB language that is supported for code generation, see Functions and Objects Supported for C/C++ Code Generation.
To help you identify unsupported functions or constructs in your MATLAB code, use one of the following tools.
Add the
%#codegen
pragma to the top of your MATLAB file. The MATLAB code analyzer flags functions and constructs that are not available in the subset of the MATLAB language supported for code generation. This advice appears in realtime as you edit your code in the MATLAB editor.For more information, see Check Code Using the MATLAB Code Analyzer.
Use the Code Generation Readiness tool to generate a static report on your code. The report identifies calls to functions and the use of data types that are not supported for code generation. To generate a report for a function,
myFunction1
, at the command line, entercoder.screener('myFunction1')
.For more information, see Check Code Using the Code Generation Readiness Tool.
Check for FixedPoint Support for Functions Used in Your Algorithm
Before you start your fixedpoint conversion, identify which functions used in your
algorithm are not supported for fixed point. Consider how you might replace them or
otherwise modify your implementation to be more optimized for embedded targets. For example,
you might need to find (or write your own) replacements for functions like
log2
, fft
, and exp
. Other
functions like sin
, cos
, and
sqrt
may support fixed point, but for better efficiency, you may want
to consider an alternative implementation like a lookup table or CORDICbased
algorithm.
If you cannot find a replacement immediately, you can continue converting the rest of your algorithm to fixed point by simply insulating any functions that don’t support fixedpoint with a cast to double at the input, and a cast back to a fixedpoint type at the output.
Original Code  Best Practice  Modified Code 

y = 1/exp(x);  Issue The
Fix Cast the
input to double until you have a replacement. In this case,

y = 1/exp(double(x)); 
Manage Data Types and Control Bit Growth
The (:)= syntax is known as subscripted assignment. When you use this syntax, MATLAB overwrites the value of the lefthand side argument, but retains the existing data type and array size. This is particularly important in keeping fixedpoint variables fixed point (as opposed to inadvertently turning them into doubles), and for preventing bit growth when you want to maintain a particular data type for the output.
Original Code  Best Practice  Modified Code 

acc = 0; for n = 1:numel(x) acc = acc + x(n); end  Issue
Fix To
preserve the original data type of 
acc = 0; for n = 1:numel(x) acc(:) = acc + x(n); end 
For more information, see Controlling Bit Growth.
Separate Data Type Definitions from Algorithm
For instrumentation and code generation, create an entrypoint function that calls the function that you want to convert to fixed point. You can then cast the function inputs to different data types. You can add calls to different variations of the function for comparison. By using an entrypoint function, you can run both fixedpoint and floatingpoint variants of your algorithm. You can also run different variants of fixedpoint. This approach allows you to iterate on your code more quickly to arrive at the optimal fixedpoint design.
This method of fixedpoint conversion makes it easier for you to compare several different fixedpoint implementations, and also allows you to easily retarget your algorithm to a different device.
To separate data type definitions from your algorithm:
When a variable is first defined, use
cast(x,'like',y)
orzeros(m,n,'like',y)
to cast it to your desired data type.Create a table of data type definitions, starting with original data types used in your code. Before converting to fixed point, create a data type table that uses all single data types to find type mismatches and other problems.
Run your code connected to each table and look at the results to verify the connection.
Original Code  Best Practice  Modified Code 

% Algorithm
n = 128;
y = zeros(size(n));
 Issue The default data type in MATLAB is doubleprecision floatingpoint. Fix

% Algorithm T = mytypes('double'); n = cast(128,'like',T.n); y = zeros(size(n),'like',T.y); function T = mytypes(dt) switch(dt) case 'double' T.n = double([]); T.y = double([]); case 'single' T.n = single([]); T.y = single([]); end end 
Separating data type specifications from algorithm code enables you to:
Reuse your algorithm code with different data types.
Keep your algorithm uncluttered with data type specifications and switch statements for different data types.
Improve readability of your algorithm code.
Switch between fixedpoint and floatingpoint data types to compare baselines.
Switch between variations of fixedpoint settings without changing the algorithm code.
Convert to Fixed Point
What Are Your Goals for Converting to Fixed Point?
Before you start the conversion, consider your goals for converting to fixed point. Are you implementing your algorithm in C or HDL? What are your target constraints? The answers to these questions determine many fixedpoint properties such as the available word length, fraction length, and math modes, as well as available math libraries.
Build and Run an Instrumented MEX Function
Build and run an instrumented MEX function to get fixedpoint types proposals using
the buildInstrumentedMex
and showInstrumentationResults
functions. Test files should exercise your
algorithm over its full operating range. The quality of the proposed fixedpoint data
types depends on how well the test file covers the operating range of the algorithm with
the accuracy that you want. A simple set of test vectors may not exercise the full range
of types, so use the proposals as a guideline for choosing an initial set of fixedpoint
types, and use your best judgement and experience in adjusting the types. If loop indices
are used only as index variables, they are automatically converted to integer types, so
you do not have to explicitly convert them to fixed point.
Algorithm Code  Test File 

function [y,z] = myfilter(b,x,z) y = zeros(size(x)); for n = 1:length(x) z(:) = [x(n); z(1:end1)]; y(n) = b * z; end end 
% Test inputs b = fir1(11,0.25); t = linspace(0,10*pi,256)'; x = sin((pi/16)*t.^2); % Linear chirp z = zeros(size(b')); % Build buildInstrumentedMex myfilter ... args {b,x,z} histogram % Run [y,z] = myfilter_mex(b,x,z); % Show showInstrumentationResults myfilter_mex ... defaultDT numerictype(1,16) proposeFL 
Create a Types Table
Create a types table using a structure with prototypes for the variables. The proposed types are computed from the simulation runs. A long simulation run with a wide range of expected data produces better proposals. You can use the proposed types or use your knowledge of the algorithm and implementation constraints to improve the proposals.
Because the data types, not the values, are used, specify the prototype values as
empty ([]
).
In some cases, it might be more efficient to leave some parts of the code in floating point. For example, when there is high dynamic range or that part of the code is sensitive to roundoff errors.
Algorithm Code  Types Tables  Test File 

function [y,z]=myfilter(b,x,z,T) y = zeros(size(x),'like',T.y); for n = 1:length(x) z(:) = [x(n); z(1:end1)]; y(n) = b * z; end end 
function T = mytypes(dt) switch dt case 'double' T.b = double([]); T.x = double([]); T.y = double([]); case 'fixed16' T.b = fi([],true,16,15); T.x = fi([],true,16,15); T.y = fi([],true,16,14); end end 
% Test inputs b = fir1(11,0.25); t = linspace(0,10*pi,256)'; x = sin((pi/16)*t.^2); % Linear chirp % Cast inputs T=mytypes('fixed16'); b=cast(b,'like',T.b); x=cast(x,'like',T.x); z=zeros(size(b'),'like',T.x); % Run [y,z] = myfilter(b,x,z,T); 
Run With FixedPoint Types and Compare Results
Create a test file to validate that the floatingpoint algorithm works as expected before converting it to fixed point. You can use the same test file to propose fixedpoint data types, and to compare fixedpoint results to the floatingpoint baseline after the conversion.
Optimize Data Types
Use Scaled Doubles
Use scaled doubles to detect potential overflows. Scaled doubles are a hybrid between
floatingpoint and fixedpoint numbers. FixedPoint Designer stores them as doubles with the scaling, sign, and word length information
retained. To use scaled doubles, you can use the data type override (DTO) property or you
can set the 'DataType'
property to 'ScaledDouble'
in
the fi
or numerictype
constructor.
To...  Use...  Example 

Set data type override locally 

T.a = fi([],1,16,13,'DataType', 'ScaledDouble'); a = cast(pi, 'like', T.a) a = 3.1416 DataTypeMode: Scaled double: binary point scaling Signedness: Signed WordLength: 16 FractionLength: 13 
Set data type override globally 

fipref('DataTypeOverride','ScaledDoubles') T.a = fi([],1,16,13); a = 3.1416 DataTypeMode:Scaled double: binary point scaling Signedness: Signed WordLength:16 FractionLength:13 
For more information, see Scaled Doubles.
Use the Histogram to FineTune Data Type Settings
To finetune fixedpoint type settings, run the buildInstrumentedMex
function with the –histogram
flag
and then run the generated MEX function with your desired test inputs. When you use the
showInstrumentationResults
to display
the code generation report, the report displays a Histogram icon. Click the icon to open
the NumericTypeScope and view the distribution of values observed in your simulation for
the selected variable.
Overflows indicated in red in the Code Generation Report show in the "outside range" bin in the NumericTypeScope. Launch the NumericTypeScope for an associated variable or expression by clicking on the histogram view icon .
Explore Design Tradeoffs
Once you have your first set of fixedpoint data types, you can then add different variations of fixedpoint values to your types table. You can modify and iterate to avoid overflows, adjust fraction lengths, and change rounding methods to eliminate bias.
Algorithm Code  Types Tables  Test File 

function [y,z] = myfilter(b,x,z,T) y = zeros(size(x),'like',T.y); for n = 1:length(x) z(:) = [x(n); z(1:end1)]; y(n) = b * z; end end 
function T = mytypes(dt) switch dt case 'double' T.b = double([]); T.x = double([]); T.y = double([]); case 'fixed8' T.b = fi([],true,8,7); T.x = fi([],true,8,7); T.y = fi([],true,8,6); case 'fixed16' T.b = fi([],true,16,15); T.x = fi([],true,16,15); T.y = fi([],true,16,14); end end 
function mytest % Test inputs b = fir1(11,0.25); t = linspace(0,10*pi,256)'; x = sin((pi/16)*t.^2); % Linear chirp % Run y0 = entrypoint('double',b,x); y8 = entrypoint('fixed8',b,x); y16 = entrypoint('fixed16',b,x); % Plot subplot(3,1,1) plot(t,x,'c',t,y0,'k') legend('Input','Baseline output') title('Baseline') subplot(3,2,3) plot(t,y8,'k') title('8bit fixedpoint output') subplot(3,2,4) plot(t,y0double(y8),'r') title('8bit fixedpoint error') subplot(3,2,5) plot(t,y16,'k') title('16bit fixedpoint output') xlabel('Time (s)') subplot(3,2,6) plot(t,y0double(y16),'r') title('16bit fixedpoint error') xlabel('Time (s)') end function [y,z] = entrypoint(dt,b,x) T = mytypes(dt); b = cast(b,'like',T.b); x = cast(x,'like',T.x); z = zeros(size(b'),'like',T.x); [y,z] = myfilter(b,x,z,T); end 
Optimize Your Algorithm
Use fimath to Get Natural Types for C or HDL
fimath
properties define the rules for performing arithmetic
operations on fi
objects, including math, rounding, and overflow
properties. You can use the fimath
ProductMode
and SumMode
properties to retain natural
data types for C and HDL. The KeepLSB
setting for
ProductMode
and SumMode
models the behavior of
integer operations in the C language, while KeepMSB
models the behavior
of many DSP devices. Different rounding methods require different amounts of overhead
code. Setting the RoundingMethod
property to Floor
,
which is equivalent to two's complement truncation, provides the most efficient rounding
implementation. Similarly, the standard method for handling overflows is to wrap using
modulo arithmetic. Other overflow handling methods create costly logic. Whenever possible,
set the OverflowAction
to Wrap
.
MATLAB Code  Best Practice  Generated C Code 

% Code being compiled function y = adder(a,b) y = a + b; end With types defined with default fimath settings: T.a = fi([],1,16,0); T.b = fi([],1,16,0); a = cast(0,'like',T.a); b = cast(0,'like',T.b);  Issue Additional code is generated to implement saturation overflow, nearest rounding, and fullprecision arithmetic. 
int adder(short a, short b) { int y; int i; int i1; int i2; int i3; i = a; i1 = b; if ((i & 65536) != 0) { i2 = i  65536; } else { i2 = i & 65535; } if ((i1 & 65536) != 0) { i3 = i1  65536; } else { i3 = i1 & 65535; } i = i2 + i3; if ((i & 65536) != 0) { y = i  65536; } else { y = i & 65535; } return y; } 
Code being compiled function y = adder(a,b) y = a + b; end With types defined with fimath settings that match your processor types: F = fimath(... 'RoundingMethod','Floor', ... 'OverflowAction','Wrap', ... 'ProductMode','KeepLSB', ... 'ProductWordLength',32, ... 'SumMode','KeepLSB', ... 'SumWordLength',32); T.a = fi([],1,16,0,F); T.b = fi([],1,16,0,F); a = cast(0,'like',T.a); b = cast(0,'like',T.b);  Fix To make the generated code more efficient, choose fixedpoint math settings that match your processor types. 
int adder(short a, short b) { return a + b; } 
Replace Builtin Functions With More Efficient FixedPoint Implementations
Some MATLAB builtin functions can be made more efficient for fixedpoint implementation. For example, you can replace a builtin function with a Lookup table implementation, or a CORDIC implementation, which requires only iterative shiftadd operations.
Reimplement Division Operations Where Possible
Often, division is not fully supported by hardware and can result in slow processing. When your algorithm requires a division, consider replacing it with one of the following options:
Use bit shifting when the denominator is a power of two. For example,
bitsra(x,3)
instead ofx/8
.Multiply by the inverse when the denominator is constant. For example,
x*0.2
instead ofx/5
.
Eliminate FloatingPoint Variables
For more efficient code, eliminate floatingpoint variables. The one exception to this is loop indices because they usually become integer types.