在 MATLAB,变量默认情况下真的是双精度的吗?

这个问题出现了一些奇怪的东西,我注意到后,研究 这个问题进一步..。

我总是理解 MATLAB 变量默认为 双精度。所以,如果我要声明一个小数点后面有20个数字的变量:

>> num = 2.71828182845904553488;
>> class(num)  % Display the variable type
ans =
double

我希望最后4位数字被忽略,因为 浮点相对精度浮点相对精度的顺序是10-16:

>> eps(num)
ans =
4.440892098500626e-016

如果我试图显示小数点后16位以上的数字(使用 fprintfsprintf) ,我得到了我期望看到的结果:

>> fprintf('%0.20f\n', num)
2.71828182845904550000
>> sprintf('%0.20f', num)
ans =
2.71828182845904550000

换句话说,数字17到20都是0。

但是当我把 num传递给 符号工具箱中的 可变精度算术函数,告诉它用21位的精度来表示数字时,事情就变得奇怪了:

>> vpa(num, 21)
ans =
2.71828182845904553488

什么? !最后四位数字又出现了!当我输入的原始数字被存储为双精度变量 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!!!
27783 次浏览

他们是替身。vpa()只是选择显示非有效数字超出浮点相对精度,其中 printf()disp()截断或零出他们。

你只能得到原来的四位数字,因为你选择的初始化 num的字面值恰好是一个二进制双精度值的精确十进制展开式,因为它是从另一个问题的实际双精度值的展开式的输出中复制粘贴过来的。正如你在“奖金”附录中所显示的那样,它不适用于附近的其他值。

更准确地说,Matlab 的所有数字字面值都产生 double 类型的值。它们被转换为最接近它们所代表的十进制值的二进制双值。实际上,超过 double 类型精度限制的文字中的数字将被静默删除。当您复制并粘贴 vpa的输出以创建一个新变量时,就像另一个问题的海报对 e = ...语句所做的那样,您是从一个文字初始化一个值,而不是直接处理前一个表达式的结果。

这里的差异只是在输出格式方面。我认为 vpa()是将双精度二进制双精度值作为精确值处理。对于给定的二进制尾数指数值,可以计算等效于任意多个小数位的小数。如果二进制值的精度有限(“宽度”) ,就像对待任何固定大小的数据类型一样,那么只有这么多十进制数字是有意义的。printf()和 Matlab 的默认显示通过截断输出或将非有效数字显示为0来处理这个问题。vpa()忽略了精度的限制,继续根据您的要求计算小数位数。

这些额外的数字是伪造的,在这个意义上,如果它们被其他值替换以产生附近的小数值,它们都将“四舍五入”到相同的二进制双值。

有个方法可以表现出来。当存储在双精度数组中时,x 的这些值都是相同的,并且用 vpa()表示也是相同的。

x = [
2.7182818284590455348848081484902650117874145507812500
2.7182818284590455348848081484902650117874145507819999
2.7182818284590455348848
2.71828182845904553488485555555555555555555555555555
exp(1)
]
unique(x)

这里还有另一种演示方法,这里有两个非常接近的替身。

x0 = exp(1)
x1 = x0 + eps(x0)

vpa(x0)vpa(x1)产生的输出应该大大超过16位数。但是,你不应该创建一个双值的 x,使得 vpa(x)产生一个介于 vpa(x0)vpa(x1)之间的小数。

(更新: Amro 指出,您可以使用 fprintf('%bx\n', x)以十六进制格式显示底层二进制值的精确表示。您可以使用它来确认字面值映射到同一个 double。)

我怀疑 vpa()之所以这样做是因为它将其输入视为精确值,并且多态地支持符号工具箱中精度高于双精度的其他 Matlab 类型。这些值需要通过数值文字以外的方式进行初始化,这就是为什么 sym()采用字符串作为输入,而 vpa(exp(1))不同于 vpa(sym('exp(1)'))的原因。

有道理吗,抱歉说了这么长时间。

(注意,我没有符号工具箱,所以我不能自己测试 vpa()。)

第一:

似乎 sprintf 和 fprintf 在不同版本的 MATLAB 上有不同的行为 例如 MATLAB 2018 a

num=2.7182818284590666666666;
sprintf('%0.70f', num)
ans =
'2.7182818284590668511668809514958411455154418945312500000000000000000000'

第二:

浮点数

MATLAB 以双精度或单精度格式表示浮点数。默认值是双精度,但是您可以通过一个简单的转换函数使任意数字成为单精度。

双精度浮点数

MATLAB 根据 IEEE 标准754构造双精度(或双精度)数据类型以实现双精度。任何存储为 double 的值都需要64位,格式如下表所示:

比特数: 63
用法: 符号(0 = 正,1 = 负)

比特: 62比52 用法: 指数,偏1023

比特: 51比0 用法: 数字1.f 的分数 f

更多信息请参考这个链接

在252 = 4,503,599,627,370,496和253 = 9,007,199,254,740,992之间,可表示的数字正好是整数。对于下一个范围,从253到254,所有数字乘以2,因此可表示的数字是偶数,等等。相反,对于前面的2 ^ 51到2 ^ 52的范围,间距是0.5,等等。

在2 ^ n 到2 ^ n + 1的范围内,数字的分数间距是2 ^ n-52。因此,将一个数字舍入到最接近的可表示数字(机器小数)时的最大相对舍入误差为2 ^ -53。

所以在 n = 1(2 ^ 1 < = num < = 2 ^ 2)的情况下,间距是2 ^ -51,

我认为可以有把握地假设 sprintf 和 sprintf 算法显示数字是棘手的,MATLAB Double 类型是基于 IEEE 标准的,


关于 VPA:

使用保护数字来保持精度

数字函数的值指定了使用的最小有效数字数。在内部,vpa 可以使用比指定数字更多的数字。这些额外的数字称为保护数字,因为它们可以防止后续计算中的舍入错误。

用四个有效数字数值接近1/3。

a = vpa(1/3, 4)
a =
0.3333

使用20个数字近似计算结果 a。结果显示,在计算。由于舍入错误,结果中的最后一位数字不正确。

vpa(a, 20)
ans =
0.33333333333303016843

你可能会遇到的问题是由于间距,后卫数字算法和舍入问题,

例如,使用 matlab 2018 a:

 sprintf('%0.28f', 8.0)
ans =
'8.0000000000000000000000000000'

但是:

sprintf('%0.28f', 8.1)
ans =
'8.0999999999999996447286321199'

因为数字在2 ^ 3和2 ^ 4之间,所以间距是2 ^ -49(= 1.77 e-15) 所以这个数字有效到小数点后15位 还有

sprintf('%0.28f', 64.1)
ans =
'64.0999999999999943156581139192'

因为数字在2 ^ 6和2 ^ 7之间,所以间距是2 ^ -46(= 1.42 e-14) 所以这个数字有效到小数点后14位