将符号表达式转换为函数时如何避免精度损失?

时间:2017-03-28 15:55:16

标签: matlab symbolic-math

tl; dr:在将符号表达式转换为matlabFunction的函数句柄时,我发现完全失去了准确性。我想知道是否有办法改善转换以避免精度的损失。

我有一个符号变量x,其中1 <= x && x <= 2成立:

syms x real
assumeAlso(1 <= x & x <= 2);

我在该变量中有许多计算机生成的,冗长的符号表达式。其中一个看起来像这样:

expression = ( ...
  1015424780204960143215273323910078528628754663952913658657288835138029704791686717561885487746105223164496264397062144000 ...
  *( ...
     60345244216851610523130575942127473698515638085026410114070496399315754724985170479034760688327679099512793302302720*2^(1/2) ...
   - 85341062796188128379389251264141456937242003828816791711306294886439805159655912635773490018204138847163921224639041 ...
   )*(x - 2)^2 ...
) ...
/31504288346872372712061562812941419427167561153216213605146864117586323331155800294021400857537041202039565879856190821729945216935526707438840242294801134287960934949194864204307116208568439380511702602881;

编辑你必须用它构建(粘贴上面的内容会使expression成为常量0)

expression = sym('(1015424780204960143215273323910078528628754663952913658657288835138029704791686717561885487746105223164496264397062144000*(60345244216851610523130575942127473698515638085026410114070496399315754724985170479034760688327679099512793302302720*2^(1/2) - 85341062796188128379389251264141456937242003828816791711306294886439805159655912635773490018204138847163921224639041)*(x - 2)^2)/31504288346872372712061562812941419427167561153216213605146864117586323331155800294021400857537041202039565879856190821729945216935526707438840242294801134287960934949194864204307116208568439380511702602881');

调用double(subs(expression, x, 1))将此表达式计算为大约-5.9492e+03,没有任何问题,这实际上是正确的值。但是,评估花费的时间太长,在我的应用程序中是一个巨大的(或相当小的?)瓶颈。这就是为什么我打算将表达式转换为一个匿名函数,它运行双精度并且速度更快,如下所示:

evaluator = matlabFunction(expression, 'Vars', x);

结果是

@(x) (sqrt(2.0).*6.034524421685161e115-8.534106279618813e115) ...
.*(x-2.0).^2.*3.223131940086397e-86

我过去在这种方法上取得了很大的成功。不幸的是,在这种情况下,对于 x 的任何值,evaluator(x)的计算结果为0,因为第一行恰好为零。显然,这与双打有限数量的有效数字有关。

有办法解决这个问题吗?我可以告诉MATLAB考虑x的范围,以便它能找到更好的常量表示吗?

1 个答案:

答案 0 :(得分:0)

问题可以通过variable-precision arithmetic解决:

>> expr = vpa(expression)
expr =
-5949.2156936801140978790460880789*(x - 2.0)^2

将其转换为函数

>> func =
  function_handle with value:
    @(x)(x-2.0).^2.*-5.949215693680114e3

然后评估

>> func(1)
ans =
  -5.9492e+03