在MATLAB中,变量真的是双精度默认? [英] In MATLAB, are variables REALLY double-precision by default?

查看:977
本文介绍了在MATLAB中,变量真的是双精度默认?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这个问题是出于某些奇怪的事情,我在研究这个问题进一步...



我总是理解MATLAB变量是双精度。所以,如果我要做一些小数点后面20位数的变量:

 >>数字= 2.71828182845904553488; 
>> class(num)%#显示变量类型
ans =
double

我期望最后4位数字被忽略,因为浮点相对精度的数量级为10 <-16> :

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

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

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

换句话说,数字17到20都是0。但是,当我将 num 传递给可变精度算术函数符号工具箱,告诉它用精度为21位的数字表示数字:

 >> vpa(num,21)
ans =
2.71828182845904553488

WHAT ?!最后4位数字重新出现!当我输入的原始数字被存储为双精度变量 num 时,它们不应该丢失吗?既然 num 在传递给 vpa 时是一个双精度变量,那么 vpa 知道它们是什么?



我最好的猜测是MATLAB在内部表示 num 比double更精确,因为我把它初始化为一个小数点后面的数字比双精度变量可以处理的数字更多。这是真的发生了什么,或者是其他事情?









奖金:如果你还没有偏头痛的话,这里还有一个混乱的来源...

 >>数字= 2.71828182845904553488; %#用小数点后20位数字声明
>>数字= 2.718281828459045531; %#重新申报十位十进制数字
>> vpa(num,21)
ans =
2.71828182845904553488%#这是原始的20位数字!


解决方案

VBA()只是简单地选择显示浮点相对精度以外的非有效数字,其中printf()和disp()截断或者将它们清零。

只重新获得你原来的四位数字,因为你选择初始化num的字面值恰好是二进制double值的确切的十进制扩展,因为它是从一个实际的double值的扩展的输出复制并粘贴的另一个问题。在你的BONUS附录中显示,它将不能用于其他附近的值。

更确切地说,Matlab中的所有数字文字都会生成double类型的值。它们被转换成最接近它们所表示的十进制值的二进制double值。实际上,超出double类型的精度限制的数字将被静默地丢弃。当你复制并粘贴vpa的输出来创建一个新的变量时,就像另一个问题的海报用e = ...语句做的那样,你正在从一个文字初始化一个值,而不是直接处理一个以前的表达。



这里的区别只是输出格式。我认为发生了什么是vpa()正在采取双精度二进制双精度和对待它作为一个确切的价值。对于给定的二进制尾数指数值,可以计算相当于任意多个小数位的小数。如果二进制值中的精度(宽度)有限,则与使用任何固定大小的数据类型时一样,只有那么多的十进制数才是重要的。 Sprintf()和Matlab的默认显示通过截断输出或者显示无效数字为0来处理这个问题。Vpa()忽略了精度的限制并继续计算你请求的小数位数。

这些额外的数字是假的,如果它们被其他值替代以产生一个附近的十进制值,它们都将被四舍五入到相同的二进制double值。



这是一个显示它的方法。这些x的值在存储在双精度时都是一样的,并且将全部由vpa()表示。

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

下面是展示它的另一种方法。这里有两个非常接近的双打。

pre $ code> x0 = exp(1)
x1 = x0 + eps(x0)

Vpa(x0)和vpa(x1)十六位数字。然而,你不应该能够创建一个double值x,使得vpa(x)产生一个介于vpa(x0)和vpa(x1)之间的十进制表示。

< (更新:Amro指出你可以使用 fprintf('%bx\\\
',x)
)以十六进制格式显示底层二进制值的确切表示。你可以使用这个来确认文本映射到同一个double。)

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



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


This question arose out of something strange that I noticed after investigating this question further...

I always understood MATLAB variables to be double-precision by default. So, if I were to do something like declare a variable with 20 digits after the decimal point:

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

I would expect the last 4 digits to be ignored, since the floating-point relative accuracy is on the order of 10-16:

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

If I try to display the number with more than 16 digits after the decimal point (using either FPRINTF or SPRINTF), I get what I expect to see:

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

In other words, digits 17 through 20 are all 0.

But things get weird when I pass num to the variable precision arithmetic function in the Symbolic Toolbox, telling it to represent the number using 21 digits of precision:

>> vpa(num,21)
ans =
2.71828182845904553488

WHAT?! Those last 4 digits have reappeared! Shouldn't they have been lost when the original number I entered was stored as a double-precision variable num? Since num is a double-precision variable when it is passed to vpa, how did vpa know what they were?

My best guess as to what is happening is that MATLAB internally represents num with more precision than a double since I initialized it to a number with more digits past the decimal point than a double-precision variable could handle. Is this really what is happening, or is something else going on?



BONUS: And here's an additional source of confusion if you don't already have a migraine from the above...

>> 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!!!

解决方案

They're doubles. Vpa() is simply choosing to display non-significant digits beyond the floating point relative accuracy, where printf() and disp() truncate or zero them out.

You're only getting your original four digits back out because the literal you chose to initialize num with just happens to be the exact decimal expansion of a binary double value, because it was copy and pasted from the output of the expansion of an actual double value from the other question. It won't work for other nearby values, as you show in your "BONUS" addendum.

More precisely, all numeric literals in Matlab produce values of type double. They get converted to the binary double value that is nearest to the decimal value they represent. In effect, digits in a literal beyond the limit of precision of the double type are silently dropped. When you copy and paste the output of vpa to create a new variable, as the other question's poster did with the "e = ..." statement, you're initializing a value from a literal, instead of dealing directly with the result of a previous expression.

The differences here are just in output formatting. I think what's going on is that vpa() is taking that double precision binary double and treating it as an exact value. For a given binary mantissa-exponent value, you can calculate the decimal equivalent to arbitrarily many decimal places. If you have a limited precision ("width") in the binary value, as you do with any fixed-size data type, only so many of those decimal digits are significant. Sprintf() and Matlab's default display handle this by truncating the output or displaying non-significant digits as 0. Vpa() is ignoring the limits of precision and continuing to calculate as many decimal places as you request.

Those additional digits are bogus, in the sense that if they were replaced by other values to produce a nearby decimal value, they would all get "rounded" to the same binary double value.

Here's a way to show it. These values of x are all the same when stored in doubles, and will all be represented the same by vpa().

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

Here's another way of demonstrating it. Here are two doubles that are very close to each other.

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

Vpa(x0) and vpa(x1) should produce outputs that differ a lot past the 16th digit. However, you shouldn't be able to create a double value x such that vpa(x) produces a decimal representation that falls between vpa(x0) and vpa(x1).

(UPDATE: Amro points out that you can use fprintf('%bx\n', x) to display an exact representation of the underlying binary value in hex format. You can use this to confirm the literals map to the same double.)

I suspect vpa() behaves this way because it treats its inputs as exact values, and polymorphically supports other Matlab types from the Symbolic Toolbox that have more precision than doubles. Those values will need to be initialized by means other than numeric literals, which is why sym() takes a string as an input and "vpa(exp(1))" differs from "vpa(sym('exp(1)'))".

Make sense? Sorry for the long-windedness.

(Note I don't have the Symbolic Toolbox so I can't test vpa() myself.)

这篇关于在MATLAB中,变量真的是双精度默认?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆