生成的代码和MATLAB代码
MATLAB转换®代码转换为高效的C/ c++代码,代码生成器引入优化,有意地导致生成的代码的行为与原始源代码不同,有时产生不同的结果。
以下是一些不同之处:
在运行时传递输入参数名(MATLAB编码器)
空重复输入参数(MATLAB编码器)
条件分配输出的输出参数验证(MATLAB编码器)
使用单精度操作数为循环索引(MATLAB编码器)
一个未输入for循环的索引(MATLAB编码器)
显示功能(MATLAB编码器)
这些差异适用于:
当你运行你生成的fiaccel
MEX, C/ c++ MEX或独立的C/ c++代码,运行时错误检查可以检测到这些差异。默认情况下,运行时错误检查对MEX代码启用,对独立的C/ c++代码禁用。为了帮助您在部署代码之前识别和处理差异,代码生成器将差异的一个子集报告为潜在的差异(MATLAB编码器).
具有多个可能输出的函数
某些数学运算,如矩阵的奇异值分解和特征值分解,可以有多个答案。实现这种操作的两种不同算法可以对相同的输入值返回不同的输出。同一算法的两个不同实现也可以表现出相同的行为。
对于这样的数学运算,生成的代码和MATLAB中对应的函数对于相同的输入值可能会返回不同的输出。若要查看函数是否具有此行为,请在相应的函数参考页中参阅C/ c++代码生成下节扩展功能.此类函数的示例包括圣言会
而且eig
.
在运行时传递输入参数名
假设喷火
使用名称-值参数验证的函数。当你打电话时喷火
从另一个函数酒吧
,代码生成器必须能够确定您提供给的名称喷火
在编译时。
如果在运行时传递参数名,在大多数情况下代码生成会失败。看到名称必须是编译时常量(MATLAB编码器).
在某些情况下,代码生成器将您传递给可选位置或重复输入参数的名称赋值。在这种情况下,代码生成成功,并发出警告,生成的代码可能会产生与MATLAB执行不同的结果。例如,考虑这个函数:
函数out = mynamedarg_warnings (a,b) out = local(a,b);结束函数Out = local(varargin,args)参数(重复)变长度输入宗量结束参数arg游戏。xarg游戏。y结束如果isfield (args,“x”) && isfield(参数,“y”) out = args。X / args.y;elseifisfield (args,“x”) out = args.x;其他的Out = varargin{1};结束结束
的行为MATLAB执行
如果你打电话myNamedArg_warns
与“x”
作为第一个输入参数,MATLAB将其与函数的第一个名称-值参数进行匹配当地的
.
myNamedArg_warns (“x”5)
Ans = 5
相比之下,如果你打电话myNamedArg_warns
与“z”
的名称-值参数都不匹配的第一个输入参数当地的
), MATLAB将输入赋值为变长度输入宗量
.
myNamedArg_warns (“z”5)
Ans = 'z'
生成代码的行为
命令尝试生成一个MEXcodegen
命令。将第一个参数的类型指定为字符标量,第二个参数指定为双标量。代码生成成功,并发出警告。
codegenmyNamedArg_warnsarg游戏{' x ', 2}
警告:此参数不是常量,因此在代码生成过程中与' mynamedarg_warnings /local'中的名称-值参数不匹配。如果在代码生成过程中不知道在调用点传递的名称,则代码生成可能会失败或产生与MATLAB不一致的结果。警告在==> mynamedarg_warnings行:2列:13代码生成成功(有警告):查看报告
不管你是否通过考试“x”
或“z”
作为第一个输入参数,生成的MEX将其分配给的第一个单元格变长度输入宗量
.
myNamedArg_warns_mex (“x”5)
Ans = 'x'
myNamedArg_warns_mex (“z”5)
Ans = 'z'
解决方案。使代码生成器能够根据函数的名称-值参数匹配第一个输入当地的
,将第一个输入声明为带值的编译时常量“x”
.方法可以做到这一点编码器。常数
函数与arg游戏
选项codegen
命令。
codegenmyNamedArg_warnsarg游戏{coder.Constant(“x”),2}
代码生成成功。
现在,生成的MEX的行为与MATLAB一致,尽管MEX不能接受除“x”
对于第一个输入。
myNamedArg_warns_mex (“x”5)
Ans = 5
myNamedArg_warns_mex (“z”5)
常量函数参数'a'的运行时值与编译时值不同。myNamedArg_warns_mex错误
空重复输入参数
在代码生成中,如果重复输入参数(在类中声明)参数
Block)在运行时为空,则该参数的大小为0 x0
.相比之下,在MATLAB执行中,一个空的重复输入参数的大小为1 x0
.
例如,考虑这个函数:
函数out = testVararginSize out = local;结束函数Out = local(varargin)参数(重复)变长度输入宗量结束Out = size(varargin);结束
运行testVararginSize
在MATLAB中返回[1 0]
.如果您为testVararginSize
并运行生成的MEX,您将得到[0 0]
.的元素迭代变长度输入宗量
通过使用长度(变长度输入宗量)
或元素个数(变长度输入宗量)
在MATLAB和代码生成中产生相同的行为。
条件分配输出的输出参数验证
如果在代码生成期间为输出参数分配了类型,代码生成器将验证输出参数。相比之下,当MATLAB函数返回时,如果参数被赋值,MATLAB执行将验证输出参数。
在大多数情况下,这种潜在的行为差异不会导致生成的代码的行为与MATLAB不同。下面是一个例子函数,你可以看到这种区别:
函数outerFunc(在)innerFunc ();结束函数out = innerFunc(inputVal)参数(输出){mustBePositive}结束如果inputVal out = inputVal;结束结束
在MATLAB中执行函数
所有双输入均成功。如果输入是正的,出
被赋给这个正值和验证器mustBePositive
运行时没有断言。如果输入为负或零,出
未赋值且未验证。
尝试生成的代码函数
.将输入类型指定为双标量。
codegenouterFuncarg游戏0
变量“out”在某些执行路径上没有完全定义。Error in ==> outerFunc Line: 7 Column: 5 Code generation failed: View Error Report
因为变量出
在一个执行路径上分配一个双标量值,代码生成分配一个双标量类型出
在编译时。代码生成器然后尝试对其执行验证出
并发现它不是完全定义的如果
失败条件。
写信给答
变量
运行返回输出而不指定输出参数的MATLAB代码时,MATLAB隐式地将输出写入答
变量。如果变量答
如果工作空间中已经存在,MATLAB将其值更新为返回的输出。
从这样的MATLAB代码生成的代码并不隐式地将输出写入答
变量。
例如,定义MATLAB函数喷火
显式地创建答
变量。函数然后隐式地更新值答
当第二行执行时。
函数喷火% # codegenAns = 1;2;disp (ans);结束
运行喷火
在命令行。的最终值答
,即2
,在命令行显示。
喷火
2
生成一个MEX函数喷火
.
codegen喷火
运行生成的MEX函数foo_mex
.此函数显式地创建答
变量并赋值1
到它。但foo_mex
的值未隐式更新答
来2
.
foo_mex
1
逻辑短路
假设你的MATLAB代码有逻辑运算符&
而且|
放在方括号内([
而且]
).对于这样的代码模式,生成的代码不为这些逻辑运算符使用短路行为,但一些MATLAB执行使用短路行为。看到提示而且提示.
例如,定义MATLAB函数喷火
它使用&
类的条件表达式中方括号内的运算符如果……
块。
函数喷火如果[returnsFalse() & hasSideEffects()]结束结束函数out = returnsFalse out = false;结束函数out = hasSideEffects out = true;disp (“这是我的绳子”);结束
的第一个参数&
运算符总是假
并确定条件表达式的值。因此,在MATLAB执行中,采用短路,不计算第二个参数。所以,喷火
不调用hasSideEffects
函数,并且不会在命令行中显示任何内容。
生成的MEX函数喷火
.调用生成的MEX函数foo_mex
.
foo_mex
这是我的字符串
在生成的代码中,不使用短路。所以,hasSideEffects
函数被调用,字符串显示在命令行上。
循环索引溢出
假设a为
-loop结束值等于或接近循环索引数据类型的最大值或最小值。在生成的代码中,循环索引的最后一个递增或递减可能导致索引变量溢出。索引溢出可能导致无限循环。
当启用内存完整性检查时,如果代码生成器检测到循环索引可能溢出,它将报告一个错误。软件错误检查比较保守。它可能错误地报告循环索引溢出。默认情况下,对MEX代码启用内存完整性检查,对独立C/ c++代码禁用内存完整性检查。看到为什么在MATLAB中测试MEX函数?(MATLAB编码器)而且生成独立的C/ c++代码来检测和报告运行时错误(MATLAB编码器).
要避免循环索引溢出,请使用本表中的变通方法。
导致潜在溢出的循环条件 | 解决方案 |
---|---|
|
如果循环不必覆盖整型的全部范围,则重写循环,使结束值不等于整型的最大值。例如,replace: N=intmax('int16') for k=N-10:N k = 1:10 |
|
如果循环不必覆盖整型的全部范围,则重写循环,使结束值不等于整型的最小值。例如,replace: N=intmin('int32') for k=N+10:-1:N k = 10: 1:1 |
|
如果循环必须覆盖整型类型的全部范围,则将循环开始值、步骤值和结束值的类型强制转换为更大的整数或加倍。例如,重写: M = intmin(“int16”);N = intmax(“int16”);对于k=M:N %环体端 M = intmin(“int16”);N = intmax(“int16”);for k=int32(M):int32(N) %循环体结束 |
|
重写循环,使最后一次循环迭代中的循环索引等于结束值。 |
索引为
使用单精度操作数进行循环
假设在你的MATLAB代码中,你索引a为
具有冒号操作符的循环,其中冒号操作数中至少有一个是单一类型操作数,且迭代次数大于Flintmax ('single') = 16777216
.当所有这些条件都为真时,代码生成可能会生成运行时或编译时错误,因为生成的代码为循环索引变量计算的值与MATLAB计算的值不同。
例如,考虑下面的MATLAB代码:
函数j = singlePIndex n = flintmax(“单一”) + 2;J =单(0);为I = single(1):single(n) j = I;结束结束
这段代码在MATLAB中执行,但它会导致编译时或运行时错误,因为循环索引变量的值,我
,在生成的代码中以不同的方式计算。代码生成器显示编译时或运行时错误,并停止代码生成或执行以防止这种差异。
要避免这种差异,请将单类型操作数替换为双类型或整型操作数。
有关运行时错误的详细信息,请参见生成独立的C/ c++代码来检测和报告运行时错误(MATLAB编码器).
Unentered的索引为
循环
在你的MATLAB代码和生成的代码之后为
循环执行完成后,索引变量的值等于其在最后迭代期间的值为
循环。
在MATLAB中,如果循环没有执行,则索引变量的值存储为[](空矩阵)。在生成的代码中,如果循环没有执行,则索引变量的值与MATLAB索引变量的值不同。
如果你提供
为
循环开始和结束变量在运行时,索引变量的值等于范围的开始。例如,考虑下面的MATLAB代码:函数out = indexTest(a,b)为I = a:b结束Out = i;结束
假设
一个
而且b
传递为1
而且-1
.的为
循环不执行。在MATLAB中,出
赋值为[]。在生成的代码中,出
赋值为一个
,即1
.如果你提供
为
循环的开始值和结束值在编译之前,索引变量的值在MATLAB和生成的代码中都被赋值[]。考虑下面的MATLAB代码:函数out = indexTest为I = 1:-1结束Out = i;结束
在MATLAB和生成的代码中,
出
赋值为[]。
字符长度
MATLAB支金宝app持16位字符,但生成的代码表示8位字符,这是大多数嵌入式语言(如c)的标准尺寸代码生成中的字符编码.
表达式的求值顺序
生成的代码不会强制表达式中的求值顺序。对于大多数表达式,求值的顺序并不重要。对于有副作用的表达式,生成的代码可能会以与原始MATLAB代码不同的顺序产生副作用。产生副作用的表达式包括:
修改持久变量或全局变量
将数据显示到屏幕上
将数据写入文件
修改句柄类对象的属性
此外,生成的代码不会强制执行不会短路的逻辑运算符的求值顺序。
为了获得更可预测的结果,将依赖于求值顺序的表达式拆分为多个语句是一种良好的编码实践。
重写
A = f1() + f2();
作为
A = f1();A = A + f2();
这样生成的代码就调用了
f1
之前f2
.将多输出函数调用的输出分配给彼此不依赖的变量。例如,重写
[y, y.f, y.g] = foo;
作为
[y, a, b] = foo;Y.f = a;Y.g = b;
当您访问单元格数组的多个单元格的内容时,请将结果分配给彼此不依赖的变量。例如,重写
[y, y.f, y.g] = z{:};
作为
[y, a, b] = z{:};Y.f = a;Y.g = b;
构造函数句柄时的名称解析
MATLAB和代码生成遵循不同的优先级规则来解析符号后面的名称@
.这些规则不适用于匿名函数。此表总结了优先级规则。
表达式 | MATLAB中的优先顺序 | 代码生成的优先顺序 |
---|---|---|
例如,不包含句点的表达式@x |
嵌套函数,局部函数,私有函数,路径函数 |
局部变量,嵌套函数,局部函数,私有函数,路径函数 |
例如,只包含一个句点的表达式@x.y |
局部变量,路径函数 |
局部变量,路径函数(同MATLAB) |
例如,包含一个以上句点的表达式@x.y.z |
路径功能 |
局部变量,路径函数 |
如果x
局部变量本身就是一个函数句柄,生成的代码和MATLAB解释的表达式@x
不同:
MATLAB产生一个错误。
生成的代码解释
@x
的函数句柄x
本身。
下面的示例显示了包含两个句点的表达式的这种行为差异。
假设您当前的工作文件夹包含一个包x
,其中包含另一个包y
,其中包含函数z
.当前工作文件夹还包含入口点函数喷火
你想为它生成代码。
这是文件的定义喷火
:
函数Out = foo X.Y.Z = @()x.y.z是一个匿名函数;Out = g(x);结束函数Out = g(x) f = @x.y.z;Out = f();结束
这就是函数的定义z
:
函数Out = z Out =x.y.z是一个包函数;结束
生成的MEX函数喷火
.分别调用两个生成的MEX函数foo_mex
和MATLAB函数喷火
.
codegen喷火foo_mex foo
Ans = 'x.y.z是一个匿名函数'
生成的代码产生第一个输出。MATLAB生成第二个输出。代码生成解决@x.y.z
到局部变量x
它被定义为喷火
.MATLAB解决@x.y.z
来z
,这是在包内x.y
.
终止的行为
生成的代码与MATLAB源代码的终止行为不匹配。例如,如果无限循环没有副作用,优化将从生成的代码中删除它们。因此,即使相应的MATLAB代码没有终止,生成的代码也可能终止。
可变大小N-D数组的大小
对于可变大小的N-D数组,大小
函数可能会在生成的代码中返回与MATLAB源代码不同的结果。的大小
函数有时在生成的代码中返回尾随数(单例维度),但在MATLAB中总是删除尾随数。例如,对于N-D数组X
与维[4 2 1 1]
,大小(X)
可能会返回[4 2 1 1]
,但总是返回(4 - 2)
在MATLAB。看到变尺寸N-D数组尺寸的确定与MATLAB不兼容.
空数组的大小
生成代码中的空数组的大小可能与MATLAB源代码中的大小不同。看到在确定空数组的大小与MATLAB不兼容.
删除数组元素导致的空数组大小
删除数组的所有元素将导致数组为空。生成代码中此空数组的大小可能与MATLAB源代码中的大小不同。
情况下 | 示例代码 | MATLAB中空数组的大小 | 生成代码中空数组的大小 |
---|---|---|---|
方法删除m × n数组的所有元素结肠 操作符(: ). |
coder.varsize (“X”(4, 4), [1]);X = 0 (2);X(:) = [];
|
0-by-0 |
1-by-0 |
方法删除行向量的所有元素结肠 操作符(: ). |
coder.varsize (“X”[1,4], [0, 1]);X = 0 (1,4);X(:) = [];
|
0-by-0 |
1-by-0 |
方法删除列向量的所有元素结肠 操作符(: ). |
coder.varsize (“X”(4,1), (1,0));X = 0 (4,1);X(:) = [];
|
0-by-0 |
0-by-1 |
通过每次删除一个元素来删除列向量的所有元素。 |
coder.varsize (“X”(4,1), (1,0));X = 0 (4,1);为i = 1:4 X(1)= [];结束 |
1-by-0 |
0-by-1 |
在运行时初始化为标量的可变大小列单元格数组
在MATLAB执行中,如果使用标量单元格数组{结束+ 1}
索引时,单元格数组沿第二维增长并生成行单元格数组。例如,定义函数growCell
:
函数z = growCell(n, m)为I = 1:m n{end+1} = m;结束Z = n;结束
调用growCell
输入示例:
growCell ({2}, 3)
Ans = 1×4单元格阵列{[2]}{[3]}{[3]}{[3]}
相比之下,在代码生成中,假设:
将单元格数组指定为可变大小的列类型(例如,
:无穷x 1
)在编译时,而且在运行时将此单元格数组初始化为标量。
在这种情况下,生成的代码将沿着第一维增长标量单元格数组并生成列单元格数组。例如,生成的MEX代码growCell
.指定输入n
成为一名:无穷x 1
以double作为基础类型的单元格数组。指定输入米
为双标量类型。
codegengrowCellarg游戏{编码器。typeof({0}, [Inf 1], [1 0]), 1}
代码生成成功。
使用与之前相同的输入运行生成的MEX。
growCell_mex ({2}, 3)
Ans = 4×1单元格阵列{[2]}{[3]}{[3]}{[3]}
具有单操作数和双操作数的二元元素操作
如果MATLAB代码包含二进制元素操作,其中涉及单类型操作数和双类型操作数,则生成的代码可能不会产生与MATLAB相同的结果。
对于这样的操作,MATLAB将两个操作数强制转换为双类型,并使用双类型执行操作。然后MATLAB将结果转换为单一类型并返回。
生成的代码将双类型操作数转换为单类型。然后,它对两个单一类型执行操作并返回结果。
例如,定义一个MATLAB函数喷火
调用二进制元素的操作+
.
函数Out = foo(a,b) Out = a + b;结束
定义一个变量s1
单一类型和一个变量v1
双型的。生成的MEX函数喷火
它接受单类型输入和双类型输入。
S1 = single(1.4e32);D1 = -5.305e+32;codegen喷火arg游戏{s1, d1}
叫两个喷火
而且foo_mex
与输入s1
而且d1
.比较这两个结果。
Ml = foo(s1,d1);MLC = foo_mex(s1,d1);Ml == MLC
Ans =逻辑0
比较的输出是逻辑的0
,这表明所生成的代码和MATLAB对这些输入产生不同的结果。
浮点数值结果
生成的代码可能不会产生与MATLAB相同的浮点数值结果:
NaN和无穷大
所生成的代码可能不会产生完全相同的模式南
而且正
当这些值在数学上没有意义时,可以用MATLAB代码来表示。例如,如果MATLAB输出包含一个南
,生成代码的输出也应该包含南
,但不一定在同一个地方。
的位模式南
MATLAB代码输出和生成的代码输出之间可能存在差异,因为用于生成代码的C99语言标准没有为南
在所有实现中。避免在不同的实现之间比较位模式,例如MATLAB输出和SIL或PIL输出之间。
负0
在浮点类型中,值0
有正号或负号。用算术方法,0
等于0
,但有些操作对0输入的符号很敏感。例子包括rdivide
,量化
,atan2d
,角
.除0
生产正
,但除以0
生产负
.同样的,atan2d (0, 1)
生产180
,但atan2d (0, 1)
生产-180年
.
如果代码生成器检测到浮点变量只接受合适范围内的整数值,则代码生成器可以在生成的代码中为该变量使用整型。如果代码生成器对变量使用整数类型,则变量存储0
作为+ 0
因为整数类型不存储值的符号0
.如果生成的代码将变量转换回浮点类型,则0
是正的。除0
生产正
,而不是负
.同样的,atan2d (0, 1)
生产180
,而不是-180年
.
生成的代码还可以处理其他上下文0
不同于MATLAB。例如,假设您的MATLAB代码计算两个标量双精度的最小值x
而且y
通过使用Z = min(x,y)
.生成的C代码中对应的行可能是Z = fmin(x,y)
.这个函数fmin
在C编译器的运行时数学库中定义。因为比较操作0.0 == -0.0
返回真正的
在C/ c++中,编译器的实现fmin
也可能返回0.0
或-0.0
为fmin (0.0, -0.0)
.
代码生成目标
的coder.target
函数在MATLAB中返回的值与在生成代码中返回的值不同。目的是帮助您确定函数是在MATLAB中执行,还是已为模拟或代码生成目标编译。看到coder.target
.
MATLAB类属性初始化
在代码生成之前,在类加载时,MATLAB计算类的默认值。代码生成器使用MATLAB计算的值。它不重新计算默认值。如果属性定义使用函数调用来计算初始值,则代码生成器不会执行此函数。如果函数有副作用,比如修改全局变量或持久变量,那么生成的代码可能会产生与MATLAB不同的结果。有关更多信息,请参见为代码生成定义类属性.
MATLAB具有Set方法的嵌套属性赋值中的类
当您将一个值赋给句柄对象属性时,该属性本身是另一个对象的属性,等等,那么生成的代码可以为MATLAB没有调用的句柄类调用set方法。
例如,假设您定义了一组这样的变量x
是句柄对象,巴勒斯坦权力机构
是一个对象,pb
句柄对象,和个人电脑
的属性。pb
.然后你做一个嵌套的属性赋值,比如:
X.pa.pb.pc = 0;
在这种情况下,生成的代码调用对象的set方法pb
和set方法x
.MATLAB只调用set方法forpb
.
MATLAB句柄类析构函数
在这些情况下,生成的代码中句柄类析构函数的行为可能与MATLAB中的行为不同:
在MATLAB中,几个独立对象的破坏顺序可能与生成的代码中不同。
生成的代码中对象的生存期可能与MATLAB中的生存期不同。
生成的代码不会破坏部分构造的对象。句柄对象在运行时未完全构造,则生成的代码将生成错误消息,但不会调用
删除
方法。对于系统对象™,如果中存在运行时错误setupImpl
时,生成的代码不调用releaseImpl
对于那个对象。MATLAB调用
删除
方法销毁部分构造的对象。
有关更多信息,请参见句柄类析构函数的代码生成.
适应数据
复数
看到复杂数据的代码生成.
将具有连续一元运算符的字符串转换为双
将包含多个连续一元运算符的字符串转换为双
可以在MATLAB和生成的代码之间产生不同的结果。考虑这个函数:
函数Out = foo(op) Out = double(op + 1);结束
对于输入值”——“
,函数转换字符串“- 1”
来双
.在MATLAB中,答案是南
.在生成的代码中,答案是1
.
显示功能
MATLAB代码中省略分号的语句和表达式隐式调用显示
函数。也可以显式调用显示
如下图:
显示(2 + 3);
5
为调用的MATLAB代码生成的MEX代码显示
函数保留对该函数的调用并显示输出。在为没有访问权限的目标生成的独立代码中MATLAB运行时的隐式和显式调用显示
是删除。的重写类方法的调用显示
.
控件可在为其他目标生成的代码中显示文本disp
函数。例如:
% MATLAB类classdef喷火方法函数Obj = foo结束函数disp(自我)disp (“disp覆盖”);结束结束结束%入口点函数函数callDisp a = foo;disp(一个);结束
入口点函数生成的代码如下所示:
/* Include Files */ # Include "callDisp.h" # Include/*函数定义*/ /* *参数:void *返回类型:void */ void callDisp(void) {printf("%s\n", "Overridden disp");。fflush (stdout);}
函数句柄差
调用显示
通过MATLAB中的函数句柄打印变量的名称。例如,在MATLAB中运行此函数会得到以下输出:
函数displayDiff z = 10;F = @display;f (z)结束
Z = 10
但是,为这个片段生成的代码只输出值10
.