Main Content

This example shows how to convert a floating-point algorithm to fixed point and then generate C code for the algorithm. The example uses the following best practices:

Separate your algorithm from the test file.

Prepare your algorithm for instrumentation and code generation.

Manage data types and control bit growth.

Separate data type definitions from algorithmic code by creating a table of data definitions.

For a complete list of best practices, see Manual Fixed-Point Conversion Best Practices.

Write a MATLAB^{®} function, `mysum`

, that sums the elements of a
vector.

function y = mysum(x) y = 0; for n = 1:length(x) y = y + x(n); end end

Since you only need to convert the algorithmic portion to fixed-point, it is more efficient to structure your code so that the algorithm, in which you do the core processing, is separate from the test file.

In the test file, create your inputs, call the algorithm, and plot the results.

Write a MATLAB script,

`mysum_test`

, that verifies the behavior of your algorithm using double data types.n = 10; rng default x = 2*rand(n,1)-1; % Algorithm y = mysum(x); % Verify results y_expected = sum(double(x)); err = double(y) - y_expected

`rng default`

puts the settings of the random number generator used by the rand function to its default value so that it produces the same random numbers as if you restarted MATLAB.Run the test script.

mysum_test

err = 0

The results obtained using

`mysum`

match those obtained using the MATLAB`sum`

function.

For more information, see Create a Test File.

In your algorithm, after the function signature, add the `%#codegen`

compilation directive to indicate that you intend to instrument the algorithm and generate C
code for it. Adding this directive instructs the MATLAB code analyzer to help you diagnose and fix violations that would result in
errors during instrumentation and code
generation.

function y = mysum(x) %#codegen y = 0; for n = 1:length(x) y = y + x(n); end end

For this algorithm, the code analyzer indicator in the top right corner of the editor window remains green telling you that it has not detected any issues.

For more information, see Prepare Your Algorithm for Code Acceleration or Code Generation.

Generate C code for the original algorithm to verify that the algorithm is suitable for
code generation and to see the floating-point C code. Use the `codegen`

(MATLAB Coder) function (requires MATLAB
Coder™) to generate a C library.

Add the following line to the end of your test script to generate C code for

`mysum`

.codegen mysum -args {x} -config:lib -report

Run the test script again.

MATLAB Coder generates C code for

`mysum`

function and provides a link to the code generation report.Click the link to open the code generation report and view the generated C code for

`mysum`

./* Function Definitions */ double mysum(const double x[10]) { double y; int n; y = 0.0; for (n = 0; n < 10; n++) { y += x[n]; } return y; }

Because C does not allow floating-point indices, the loop counter,

`n`

, is automatically declared as an integer type. You do not need to convert`n`

to fixed point.Input

`x`

and output`y`

are declared as double.

Test Your Algorithm With Singles to Check for Type Mismatches

Modify your test file so that the data type of

`x`

is single.n = 10; rng default x = single(2*rand(n,1)-1); % Algorithm y = mysum(x); % Verify results y_expected = sum(double(x)); err = double(y) - y_expected codegen mysum -args {x} -config:lib -report

Run the test script again.

mysum_test

err = -4.4703e-08 ??? This assignment writes a 'single' value into a 'double' type. Code generation does not support changing types through assignment. Check preceding assignments or input type specifications for type mismatches.

Code generation fails, reporting a data type mismatch on line

`y = y + x(n);`

.To view the error, open the report.

In the report, on the line

`y = y + x(n)`

, the report highlights the`y`

on the left side of the assignment in red to indicate that there is an error. The issue is that`y`

is declared as a double but is being assigned to a single.`y + x(n)`

is the sum of a double and a single which is a single. If you place your cursor over variables and expressions in the report, you can see information about their types. Here, you can see that the expression,`y + x(n)`

is a single.To fix the type mismatch, update your algorithm to use subscripted assignment for the sum of elements. Change

`y = y + x(n)`

to`y(:) = y + x(n)`

.function y = mysum(x) %#codegen y = 0; for n = 1:length(x) y(:) = y + x(n); end end

Using subscripted assignment, you also prevent the bit growth which is the default behavior when you add fixed-point numbers. For more information, see Bit Growth. Preventing bit growth is important because you want to maintain your fixed-point types throughout your code. For more information, see Controlling Bit Growth.

Regenerate C code and open the code generation report. In the C code, the result is now cast to double to resolve the type mismatch.

Use the `buildInstrumentedMex`

function to instrument
your algorithm for logging minimum and maximum values of all named and intermediate variables.
Use the `showInstrumentationResults`

function to
propose fixed-point data types based on these logged values. Later, you use these proposed
fixed-point types to test your algorithm.

Update the test script:

After you declare

`n`

, add`buildInstrumentedMex mySum —args {zeros(n,1)} -histogram`

.Change

`x`

back to double. Replace`x = single(2*rand(n,1)-1);`

with`x = 2*rand(n,1)-1;`

Instead of calling the original algorithm, call the generated MEX function. Change

`y = mysum(x)`

to`y=mysum_mex(x)`

.After calling the MEX function, add

`showInstrumentationResults mysum_mex -defaultDT numerictype(1,16) -proposeFL`

. The`-defaultDT numerictype(1,16) -proposeFL`

flags indicate that you want to propose fraction lengths for a 16-bit word length.Here is an updated test script.

%% Build instrumented mex n = 10; buildInstrumentedMex mysum -args {zeros(n,1)} -histogram %% Test inputs rng default x = 2*rand(n,1)-1; % Algorithm y = mysum_mex(x); % Verify results showInstrumentationResults mysum_mex ... -defaultDT numerictype(1,16) -proposeFL y_expected = sum(double(x)); err = double(y) - y_expected %% Generate C code codegen mysum -args {x} -config:lib -report

Run the test script again.

The

`showInstrumentationResults`

function proposes data types and opens a report to display the results.In the report, click the

**Variables**tab.`showInstrumentationResults`

proposes a fraction length of 13 for`y`

and 15 for`x`

.

In the report, you can:

View the simulation minimum and maximum values for the input

`x`

and output`y`

.View the proposed data types for

`x`

and`y`

.View information for all variables, intermediate results, and expressions in your code.

To view this information, place your cursor over the variable or expression in the report.

View the histogram data for

`x`

and`y`

to help you identify any values that are outside range or below precision based on the current data type.To view the histogram for a particular variable, click its histogram icon, .

Rather than manually modifying the algorithm to examine the behavior for each data type, separate the data type definitions from the algorithm.

Modify `mysum`

so that it takes an input parameter, `T`

,
which is a structure that defines the data types of the input and output data. When
`y`

is first defined, use the `cast`

function like syntax — `cast(x,'like',y)`

—
to cast `x`

to the desired data
type.

function y = mysum(x,T) %#codegen y = cast(0,'like',T.y); for n = 1:length(x) y(:) = y + x(n); end end

Write a function, `mytypes`

, that defines the different data types that
you want to use to test your algorithm. In your data types table, include double, single, and
scaled double data types as well as the fixed-point data types proposed earlier. Before
converting your algorithm to fixed point, it is good practice to:

Test the connection between the data type definition table and your algorithm using doubles.

Test the algorithm with singles to find data type mismatches and other problems.

Run the algorithm using scaled doubles to check for overflows.

function T = mytypes(dt) switch dt case 'double' T.x = double([]); T.y = double([]); case 'single' T.x = single([]); T.y = single([]); case 'fixed' T.x = fi([],true,16,15); T.y = fi([],true,16,13); case 'scaled' T.x = fi([],true,16,15,... 'DataType','ScaledDouble'); T.y = fi([],true,16,13,... 'DataType','ScaledDouble'); end end

For more information, see Separate Data Type Definitions from Algorithm.

Update the test script, `mysum_test`

, to use the types table.

For the first run, check the connection between table and algorithm using doubles. Before you declare

`n`

, add`T = mytypes('double');`

Update the call to

`buildInstrumentedMex`

to use the type of`T.x`

specified in the data types table:`buildInstrumentedMex mysum -args {zeros(n,1,'like',T.x),T} -histogram`

Cast

`x`

to use the type of`T.x`

specified in the table:`x = cast(2*rand(n,1)-1,'like',T.x);`

Call the MEX function passing in

`T`

:`y = mysum_mex(x,T);`

Call

`codegen`

passing in`T`

:`codegen mysum -args {x,T} -config:lib -report`

Here is the updated test script.

%% Build instrumented mex T = mytypes('double'); n = 10; buildInstrumentedMex mysum ... -args {zeros(n,1,'like',T.x),T} -histogram %% Test inputs rng default x = cast(2*rand(n,1)-1,'like',T.x); % Algorithm y = mysum_mex(x,T); % Verify results showInstrumentationResults mysum_mex ... -defaultDT numerictype(1,16) -proposeFL y_expected = sum(double(x)); err = double(y) - y_expected %% Generate C code codegen mysum -args {x,T} -config:lib -report

Run the test script and click the link to open the code generation report.

The generated C code is the same as the code generated for the original algorithm. Because the variable

`T`

is used to specify the types and these types are constant at code generation time;`T`

is not used at run time and does not appear in the generated code.

Update the test script to use the fixed-point types proposed earlier and view the generated C code.

Update the test script to use fixed-point types. Replace

`T = mytypes('double');`

with`T = mytypes('fixed');`

and then save the script.Run the test script and view the generated C code.

This version of C code is not very efficient; it contains a lot of overflow handling. The next step is to optimize the data types to avoid overflows.

Scaled doubles are a hybrid between floating-point and fixed-point numbers. Fixed-Point Designer™ stores them as doubles with the scaling, sign, and word length information retained. Because all the arithmetic is performed in double-precision, you can see any overflows that occur.

Update the test script to use scaled doubles. Replace

`T = mytypes('fixed');`

with`T = mytypes('scaled');`

Run the test script again.

The test runs using scaled doubles and displays the report. No overflows are detected.

So far, you’ve run the test script using random inputs which means that it is unlikely that the test has exercised the full operating range of the algorithm.

Find the full range of the input.

range(T.x)

-1.000000000000000 0.999969482421875 DataTypeMode: Fixed-point: binary point scaling Signedness: Signed WordLength: 16 FractionLength: 15

Update the script to test the negative edge case. Run

`mysum_mex`

with the original random input and with an input that tests the full range and aggregate the results.%% Build instrumented mex T = mytypes('scaled'); n = 10; buildInstrumentedMex mysum ... -args {zeros(n,1,'like',T.x),T} -histogram %% Test inputs rng default x = cast(2*rand(n,1)-1,'like',T.x); y = mysum_mex(x,T); % Run once with this set of inputs y_expected = sum(double(x)); err = double(y) - y_expected % Run again with this set of inputs. The logs will aggregate. x = -ones(n,1,'like',T.x); y = mysum_mex(x,T); y_expected = sum(double(x)); err = double(y) - y_expected % Verify results showInstrumentationResults mysum_mex ... -defaultDT numerictype(1,16) -proposeFL y_expected = sum(double(x)); err = double(y) - y_expected %% Generate C code codegen mysum -args {x,T} -config:lib -report

Run the test script again.

The test runs and

`y`

overflows the range of the fixed-point data type.`showInstrumentationResults`

proposes a new fraction length of 11 for`y`

.Update the test script to use scaled doubles with the new proposed type for

`y`

. In`myTypes.m`

, for the`'scaled'`

case,`T.y = fi([],true,16,11,'DataType','ScaledDouble')`

Rerun the test script.

There are now no overflows.

Update the data types table to use the proposed fixed-point type and generate code.

In

`myTypes.m`

, for the`'fixed'`

case,`T.y = fi([],true,16,11)`

Update the test script,

`mysum_test`

, to use`T = mytypes('fixed');`

Run the test script and then click the View Report link to view the generated C code.

short mysum(const short x[10]) { short y; int n; int i; int i1; int i2; int i3; y = 0; for (n = 0; n < 10; n++) { i = y << 4; i1 = x[n]; if ((i & 1048576) != 0) { i2 = i | -1048576; } else { i2 = i & 1048575; } if ((i1 & 1048576) != 0) { i3 = i1 | -1048576; } else { i3 = i1 & 1048575; } i = i2 + i3; if ((i & 1048576) != 0) { i |= -1048576; } else { i &= 1048575; } i = (i + 8) >> 4; if (i > 32767) { i = 32767; } else { if (i < -32768) { i = -32768; } } y = (short)i; } return y; }

By default,

`fi`

arithmetic uses saturation on overflow and nearest rounding which results in inefficient code.

To make the generated code more efficient, use fixed-point math
(`fimath`

) settings that are more appropriate for C code generation: wrap
on overflow and floor rounding.

In

`myTypes.m`

, add a`'fixed2'`

case:case 'fixed2' F = fimath('RoundingMethod', 'Floor', ... 'OverflowAction', 'Wrap', ... 'ProductMode', 'FullPrecision', ... 'SumMode', 'KeepLSB', ... 'SumWordLength', 32, ... 'CastBeforeSum', true); T.x = fi([],true,16,15,F); T.y = fi([],true,16,11,F);

**Tip**Instead of manually entering

`fimath`

properties, you can use the MATLAB Editor**Insert fimath**option. For more information, see Building fimath Object Constructors in a GUI.Update the test script to use

`'fixed2'`

, run the script, and then view the generated C code.short mysum(const short x[10]) { short y; int n; y = 0; for (n = 0; n < 10; n++) { y = (short)(((y << 4) + x[n]) >> 4); } return y; }

The generated code is more efficient, but

`y`

is shifted to align with`x`

and loses 4 bits of precision.To fix this precision loss, update the word length of

`y`

to 32 bits and keep 15 bits of precision to align with`x`

.In

`myTypes.m`

, add a`'fixed32'`

case:case 'fixed32' F = fimath('RoundingMethod', 'Floor', ... 'OverflowAction', 'Wrap', ... 'ProductMode', 'FullPrecision', ... 'SumMode', 'KeepLSB', ... 'SumWordLength', 32, ... 'CastBeforeSum', true); T.x = fi([],true,16,15,F); T.y = fi([],true,32,15,F);

Update the test script to use

`'fixed32'`

and run the script to generate code again.Now, the generated code is very efficient.

int mysum(const short x[10]) { int y; int n; y = 0; for (n = 0; n < 10; n++) { y += x[n]; } return y; }

For more information, see Optimize Your Algorithm.