gno*_*ice 72 variables floating-point precision matlab
这个问题产生于我在进一步调查这个问题后发现的一些奇怪的事情......
我总是将MATLAB变量理解为默认的双精度.所以,如果我要做一些事情,比如声明一个小数点后20位数的变量:
>> num = 2.71828182845904553488;
>> class(num) % Display the variable type
ans =
double
Run Code Online (Sandbox Code Playgroud)
我希望忽略最后4位数,因为浮点相对精度大约为10 -16:
>> eps(num)
ans =
4.440892098500626e-016
Run Code Online (Sandbox Code Playgroud)
如果我尝试在小数点后面显示超过16位的数字(使用fprintf或者sprintf),我得到的结果是:
>> fprintf('%0.20f\n', num)
2.71828182845904550000
>> sprintf('%0.20f', num)
ans =
2.71828182845904550000
Run Code Online (Sandbox Code Playgroud)
换句话说,数字17到20都是0.
但是,事情变得奇怪,当我传球num给可变精度算术函数的符号工具箱,告诉它表示使用的精度21个的位数:
>> vpa(num, 21)
ans =
2.71828182845904553488
Run Code Online (Sandbox Code Playgroud)
什么?!最后4位数字再次出现!当我输入的原始数字存储为双精度变量时,它们是否应该丢失num?既然num传递给它是一个双精度变量vpa,怎么vpa知道它们是什么?
我最好的猜测是,MATLAB内部表示的num精度高于双精度,因为我将它初始化为一个数字,其数字超过小数点,而不是双精度变量可以处理的数字.这真的是发生了什么,还是正在发生的事情?
奖励:如果你还没有上述的偏头痛,这里还有另外一个混乱的根源......
>> num = 2.71828182845904553488; % Declare with 20 digits past the decimal
>> num = 2.718281828459045531; % Re-declare with 18 digits past the decimal
>> vpa(num, 21)
ans =
2.71828182845904553488 % It's the original 20-digit number!!!
Run Code Online (Sandbox Code Playgroud)
And*_*nke 65
他们是双打的.vpa()只是选择显示超出浮点相对精度的非有效数字,printf()并将其disp()截断或归零.
你只是将原来的四位数字退出,因为你选择初始化的文字num恰好是二进制双精度值的精确十进制扩展,因为它是从实际双精度值的扩展输出中复制和粘贴的从另一个问题.它不适用于附近的其他值,正如您在"奖励"附录中所示.
更准确地说,Matlab中的所有数字文字都会生成double类型的值.它们被转换为二进制double值,该值最接近它们所代表的十进制值.实际上,超出double类型精度限制的文字中的数字会被静默删除.当您复制并粘贴输出vpa以创建新变量时,正如另一个问题的海报对该e = ...语句所做的那样,您正在从文字初始化值,而不是直接处理前一个表达式的结果.
这里的差异只是输出格式.我认为正在发生的vpa()是将双精度二进制加倍并将其视为精确值.对于给定的二进制尾数 - 指数值,您可以计算等效于任意多个小数位的十进制等值.如果您在二进制值中具有有限的精度("宽度"),就像使用任何固定大小的数据类型一样,只有那么多的十进制数字是重要的.printf()和Matlab的默认显示通过截断输出或将非有效数字显示为0 vpa()来处理此操作.忽略精度限制并继续计算您请求的小数位数.
那些额外的数字是假的,在某种意义上,如果它们被其他值替换以产生附近的十进制值,它们将全部"舍入"到相同的二进制双值.
这是展示它的一种方式.当存储在双精度中时,这些x的值都是相同的,并且将全部表示为相同的vpa().
x = [
2.7182818284590455348848081484902650117874145507812500
2.7182818284590455348848081484902650117874145507819999
2.7182818284590455348848
2.71828182845904553488485555555555555555555555555555
exp(1)
]
unique(x)
Run Code Online (Sandbox Code Playgroud)
这是展示它的另一种方式.这里有两个非常接近的双打.
x0 = exp(1)
x1 = x0 + eps(x0)
Run Code Online (Sandbox Code Playgroud)
vpa(x0)并且vpa(x1)应该产生与第16位数相差很多的输出.但是,您不能创建一个double值x,以便vpa(x)生成一个介于vpa(x0)和之间的十进制表示vpa(x1).
(更新:Amro指出您可以使用fprintf('%bx\n', x)以十六进制格式显示基础二进制值的精确表示.您可以使用它来确认文字映射到同一个double.)
我怀疑vpa()这样做是因为它将输入视为精确值,并且多态支持符号工具箱中比精度更高的其他Matlab类型.这些值需要通过数字文字以外的方式初始化,这就是为什么sym()将字符串作为输入而vpa(exp(1))不同的原因vpa(sym('exp(1)')).
合理?对不起,啰嗦.
(注意我没有符号工具箱所以我无法测试vpa()自己.)
| 归档时间: |
|
| 查看次数: |
26861 次 |
| 最近记录: |