この質問は この質問 さらに...
MATLAB変数は、デフォルトでは double-precision であると常に理解していました。そのため、小数点以下20桁の変数を宣言するようなことをするとしたら、
>> num = 2.71828182845904553488;
>> class(num) % Display the variable type
ans =
double
浮動小数点の相対精度 は10のオーダーであるため、最後の4桁は無視されると予想されます-16:
>> eps(num)
ans =
4.440892098500626e-016
小数点以下16桁以上の数字を表示しようとすると( fprintf
または sprintf
を使用)、私が期待するもの:
>> fprintf('%0.20f\n', num)
2.71828182845904550000
>> sprintf('%0.20f', num)
ans =
2.71828182845904550000
つまり、17〜20の数字はすべて0です。
しかし、num
を Symbolic Toolbox の 可変精度の算術関数 に渡して、21桁の精度を使用して数値を表すように指示すると、事態は奇妙になります。
>> vpa(num, 21)
ans =
2.71828182845904553488
何?!最後の4桁が再び表示されました!入力した元の数値が倍精度変数num
として保存されていたときに失われてはいけませんか? num
はvpa
に渡されるときに倍精度変数であるため、vpa
はどのようにそれらが何であるかを知りましたか?
何が起こっているのかについての最良の推測は、倍精度変数が処理できるよりも小数点以下の桁数が多い数値に初期化したため、MATLABは内部的にnum
をdoubleよりも高い精度で表していることです。これは実際に何が起こっているのか、それとも何かが起こっているのか?
ボーナス:そして、上記の片頭痛をまだ持っていない場合の混乱の原因はここにあります...
>> 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!!!
彼らはダブルです。 vpa()
は、浮動小数点の相対精度を超えて有効でない桁を表示することを選択するだけです。ここで、printf()
とdisp()
はそれらを切り捨てまたはゼロにします。
num
を初期化するために選択したリテラルはたまたまバイナリdouble値の正確な10進展開であるため、元の4桁のみが戻されます。他の質問からの実際のdouble値の展開。 「BONUS」補遺に示すように、他の近くの値では機能しません。
より正確には、Matlabのすべての数値リテラルは、double型の値を生成します。それらは、それらが表す10進値に最も近い2進double値に変換されます。実際には、double型の精度の制限を超えるリテラル内の数字は、暗黙のうちに削除されます。他の質問の投稿者がe = ...
ステートメントで行ったように、vpa
の出力をコピーして貼り付けて新しい変数を作成すると、直接処理するのではなく、リテラルから値を初期化しています前の式の結果。
ここでの違いは、出力のフォーマットだけです。起こっているのは、vpa()
がその倍精度のバイナリdoubleを取り、それを正確な値として扱っていることだと思います。与えられたバイナリ仮数指数値について、任意の多くの小数位に相当する小数を計算できます。任意の固定サイズのデータ型と同様に、バイナリ値の精度(「幅」)が制限されている場合、これらの10進数字の多くだけが重要です。 printf()
およびMatlabのデフォルトのディスプレイは、出力を切り捨てるか、または有効でない数字を0として表示することでこれを処理します。vpa()
は精度の制限を無視し、要求した数だけ小数点以下を計算し続けます。
これらの追加の数字は、近くの10進数値を生成するために他の値に置き換えられた場合、すべて同じ2進数のdouble値に「丸められる」という意味で偽です。
これを表示する方法を次に示します。 xのこれらの値は、doubleに格納されるとすべて同じであり、vpa()
ですべて同じように表されます。
x = [
2.7182818284590455348848081484902650117874145507812500
2.7182818284590455348848081484902650117874145507819999
2.7182818284590455348848
2.71828182845904553488485555555555555555555555555555
exp(1)
]
unique(x)
これを示す別の方法があります。これは、互いに非常に近い2つのdoubleです。
x0 = exp(1)
x1 = x0 + eps(x0)
vpa(x0)
およびvpa(x1)
は、16桁を大きく超える出力を生成する必要があります。ただし、vpa(x)
がvpa(x0)
とvpa(x1)
の間にある10進表現を生成するような二重値x
を作成することはできません。
(更新:Amroは、fprintf('%bx\n', x)
を使用して、基になるバイナリ値の正確な表現を16進形式で表示できることを指摘しています。これを使用して、リテラルが同じdoubleにマップされることを確認できます。)
vpa()
は、入力を正確な値として扱い、倍精度よりも高い精度を持つSymbolic Toolboxの他のMatlabタイプを多態的にサポートするため、このように動作すると思われます。これらの値は、数値リテラル以外の手段で初期化する必要があります。そのため、sym()
は入力として文字列を取り、vpa(exp(1))
はvpa(sym('exp(1)'))
と異なります。
理にかなっていますか?苦労して申し訳ありません。
(注:Symbolic Toolboxがないため、vpa()
を自分でテストすることはできません。)
最初 :
sprintfとfprintfは、MATLAB 2018 aなどのMATLABの異なるバージョンで異なる動作をするようです
num=2.7182818284590666666666;
sprintf('%0.70f', num)
ans =
'2.7182818284590668511668809514958411455154418945312500000000000000000000'
第二:
浮動小数点数
MATLAB®は、倍精度形式または単精度形式で浮動小数点数を表します。デフォルトは倍精度ですが、単純な変換関数を使用して任意の数値を単精度にすることができます。
倍精度浮動小数点
MATLABは、倍精度のIEEE®Standard 754に従って、倍精度(またはdouble)データ型を構築します。 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は精度を維持するためにガードディジットを使用します
ディジット関数の値は、使用される有効ディジットの最小数を指定します。内部的に、vpaは数字が指定するよりも多くの数字を使用できます。これらの追加の数字は、後続の計算での丸め誤差を防ぐため、保護数字と呼ばれます。
4つの有効数字を使用して、1/3を数値的に近似します。
a = vpa(1/3, 4)
a =
0.3333
20桁を使用して結果aを近似します。結果は、ツールボックスがaを計算するときに内部で4桁以上を使用したことを示しています。結果の最後の桁は、丸め誤差のために正しくありません。
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位まで有効です。