使用 vpa() 时 mod() 的 MATLAB 奇怪行为
MATLAB strange behaviour with mod() when using vpa()
如果我们有如下代码:
base = vpa(1);
height = vpa(2.2);
mod(2*base + height + height, 2 * (base + height))
这将产生 6.4 的输出。我希望结果为 0,而数值解确实给出 0。但我需要使用带有 vpa()
.
的符号值
我做了一些实验来找出原因并发现:
simplify(2*base + height + height < 6.4)
simplify(2 * (base + height) == 6.4)
都给TRUE
。所以相同的(数字)表达式更小,等于 6.4。
我应该怎么做才能解决这个问题并得到 0 的答案?是什么导致了这个问题?
mod(2*base + height + height, base+base+height+height)
这行代码生成 0。仍然不确定到底是什么导致了幕后问题。 product 和 sum 之间似乎有明显的区别,因为以下代码行是 FALSE
mod(base+base+height+height, 2*(base+height))
问题是 vpa
提供任意大的精度,但 不精确 。首先,请注意 vpa
with a second input uses digits
精度数字,默认情况下为 32
。当你这样做时
height = vpa(2.2)
这与
相同
height = vpa(2.2, 32)
假设 digits
是 32
。因此 height
将具有 32
位精度,但并不精确。要看到这一点,请更精确地评估定义的 height
:
>> vpa(height, 32)
ans =
2.2
>> vpa(height, 40)
ans =
2.2
>> vpa(height, 50)
ans =
2.2000000000000000000000000000000000000001469367939
这种不准确性在 2*base + height + height
和 2 * (base + height)
之间引入了数值差异,两者实际上都不等于 6.4
:
>> base = vpa(1);
>> height = vpa(2.2);
>> vpa(2*base + height + height, 50)
ans =
6.3999999999999999999999999999999999999988245056492
>> vpa(2 * (base + height), 50)
ans =
6.4000000000000000000000000000000000000002938735877
因此,即使 mod(2*base + height + height, 2 * (base + height))
看起来 是 0
>> mod(2*base + height + height, 2 * (base + height))
ans =
6.4
but it's _not_:
>> vpa(mod(2*base + height + height, 2 * (base + height)), 50)
ans =
6.3999999999999999999999999999999999999988245056492
请注意,后一个结果中与 6.4
的偏差不等于上述两个小偏差的总和;相反,它等于第一个。数值不准确不保证是相加的。
简而言之,vpa
减少但不能完全避免数值精度错误。
如果我们增加用于 vpa
的位数会怎样?这会提供更高的精度并可能解决问题吗?
>> base = vpa(1, 1000);
>> height = vpa(2.2, 1000);
奇怪的是,尽管使用了更多的数字,我们还是得到了和以前一样的不准确:
>> vpa(2*base + height + height, 50)
ans =
6.3999999999999999999999999999999999999988245056492
>> vpa(2 * (base + height), 50)
ans =
6.4000000000000000000000000000000000000002938735877
>> vpa(mod(2*base + height + height, 2 * (base + height)), 50)
ans =
6.3999999999999999999999999999999999999988245056492
因此,避免精度错误的唯一方法是将 vpa
替换为 符号变量 ,即 exact:
>> base = sym(1)
base =
1
>> height = sym(2.2)
height =
11/5
关于如何定义符号变量的题外话是有序的。请注意,sym(2.2)
有一个潜在的问题,即它 首先 将 2.2
定义为 double
浮点数,其固有的不准确性,并且 then即转换为sym
。在这种情况下,这不是问题,因为 2.2
的浮点数值表示恰好是精确的。事实上,我们可以检查 Matlab 显示 height = sym(2.2)
,这是准确的。此外,即使 double
表示不准确,Matlab 也会尝试 猜测 您的意图,并且通常会成功:
>> sym(3.141592653589793)
ans =
pi
(Matlab 假设我们指的是数字 pi)...但并非总是如此:
>> sym(sqrt(777))
ans =
777^(1/2)
>> sym(sqrt(777777))
ans =
7757421003204227/8796093022208
(sqrt(777)
给出 double
结果 27.874719729532707
,它被 sym
识别为 777
的平方根的近似值。在另一方面,sqrt(777777)
给出 8.819166627295348e+02
,sym
无法将其识别为 777777
的平方根的近似值)。
>> sym(7777)
ans =
7777
>> sym(7777777777777777777)
ans =
7777777777777777664
(7777
正好表示为 double
,但 7777777777777777777
不是,因为它超过了 2^53
。)
所以,可以肯定的是,安全的定义符号变量的方法是只使用小整数,其他符号变量,或精确的字符串表示:
>> height = sym('22/10')
height =
11/5
现在,base
和 height
被正确定义为符号变量,我们得到了预期的结果:
>> mod(2*base + height + height, 2 * (base + height))
ans =
0
>> vpa(mod(2*base + height + height, 2 * (base + height)), 1000)
ans =
0.0
如果我们有如下代码:
base = vpa(1);
height = vpa(2.2);
mod(2*base + height + height, 2 * (base + height))
这将产生 6.4 的输出。我希望结果为 0,而数值解确实给出 0。但我需要使用带有 vpa()
.
我做了一些实验来找出原因并发现:
simplify(2*base + height + height < 6.4)
simplify(2 * (base + height) == 6.4)
都给TRUE
。所以相同的(数字)表达式更小,等于 6.4。
我应该怎么做才能解决这个问题并得到 0 的答案?是什么导致了这个问题?
mod(2*base + height + height, base+base+height+height)
这行代码生成 0。仍然不确定到底是什么导致了幕后问题。 product 和 sum 之间似乎有明显的区别,因为以下代码行是 FALSE
mod(base+base+height+height, 2*(base+height))
问题是 vpa
提供任意大的精度,但 不精确 。首先,请注意 vpa
with a second input uses digits
精度数字,默认情况下为 32
。当你这样做时
height = vpa(2.2)
这与
相同height = vpa(2.2, 32)
假设 digits
是 32
。因此 height
将具有 32
位精度,但并不精确。要看到这一点,请更精确地评估定义的 height
:
>> vpa(height, 32)
ans =
2.2
>> vpa(height, 40)
ans =
2.2
>> vpa(height, 50)
ans =
2.2000000000000000000000000000000000000001469367939
这种不准确性在 2*base + height + height
和 2 * (base + height)
之间引入了数值差异,两者实际上都不等于 6.4
:
>> base = vpa(1);
>> height = vpa(2.2);
>> vpa(2*base + height + height, 50)
ans =
6.3999999999999999999999999999999999999988245056492
>> vpa(2 * (base + height), 50)
ans =
6.4000000000000000000000000000000000000002938735877
因此,即使 mod(2*base + height + height, 2 * (base + height))
看起来 是 0
>> mod(2*base + height + height, 2 * (base + height))
ans =
6.4
but it's _not_:
>> vpa(mod(2*base + height + height, 2 * (base + height)), 50)
ans =
6.3999999999999999999999999999999999999988245056492
请注意,后一个结果中与 6.4
的偏差不等于上述两个小偏差的总和;相反,它等于第一个。数值不准确不保证是相加的。
简而言之,vpa
减少但不能完全避免数值精度错误。
如果我们增加用于 vpa
的位数会怎样?这会提供更高的精度并可能解决问题吗?
>> base = vpa(1, 1000);
>> height = vpa(2.2, 1000);
奇怪的是,尽管使用了更多的数字,我们还是得到了和以前一样的不准确:
>> vpa(2*base + height + height, 50)
ans =
6.3999999999999999999999999999999999999988245056492
>> vpa(2 * (base + height), 50)
ans =
6.4000000000000000000000000000000000000002938735877
>> vpa(mod(2*base + height + height, 2 * (base + height)), 50)
ans =
6.3999999999999999999999999999999999999988245056492
因此,避免精度错误的唯一方法是将 vpa
替换为 符号变量 ,即 exact:
>> base = sym(1)
base =
1
>> height = sym(2.2)
height =
11/5
关于如何定义符号变量的题外话是有序的。请注意,sym(2.2)
有一个潜在的问题,即它 首先 将 2.2
定义为 double
浮点数,其固有的不准确性,并且 then即转换为sym
。在这种情况下,这不是问题,因为 2.2
的浮点数值表示恰好是精确的。事实上,我们可以检查 Matlab 显示 height = sym(2.2)
,这是准确的。此外,即使 double
表示不准确,Matlab 也会尝试 猜测 您的意图,并且通常会成功:
>> sym(3.141592653589793)
ans =
pi
(Matlab 假设我们指的是数字 pi)...但并非总是如此:
>> sym(sqrt(777))
ans =
777^(1/2)
>> sym(sqrt(777777))
ans =
7757421003204227/8796093022208
(sqrt(777)
给出 double
结果 27.874719729532707
,它被 sym
识别为 777
的平方根的近似值。在另一方面,sqrt(777777)
给出 8.819166627295348e+02
,sym
无法将其识别为 777777
的平方根的近似值)。
>> sym(7777)
ans =
7777
>> sym(7777777777777777777)
ans =
7777777777777777664
(7777
正好表示为 double
,但 7777777777777777777
不是,因为它超过了 2^53
。)
所以,可以肯定的是,安全的定义符号变量的方法是只使用小整数,其他符号变量,或精确的字符串表示:
>> height = sym('22/10')
height =
11/5
现在,base
和 height
被正确定义为符号变量,我们得到了预期的结果:
>> mod(2*base + height + height, 2 * (base + height))
ans =
0
>> vpa(mod(2*base + height + height, 2 * (base + height)), 1000)
ans =
0.0