私は C を勉強していて、ガード桁と丸め誤差のアイデアが浮かびました。スクリプト言語 (ここでは Python と Perl を考えています) の実践者は、このようなことについて心配する必要がありますか? 彼らが科学的プログラミングを行っている場合はどうなりますか?
9 に答える
場合によります。 double
sはどこでも同じように動作するため、doubleを使用して数学を行う場合、どの言語でも同じ問題が発生します。ネイティブの任意精度タイプを使用する場合、いいえ、問題ありません。検討:
use Math::BigFloat;
my $big = Math::BigFloat->new("1_000_000_000_000_000_000_000");
my $small = Math::BigFloat->new("0.000000000000000000000000001");
print $big + $small;
(または、本当に何が起こっているのかを隠したい場合:
use bignum;
print 1_000_000_000_000_000_000_000 + 0.000000000000000000000000001
)。
予想どおり、これにより次の結果が得られます。
1000000000000000000000.000000000000000000000000001
また、予想どおり、これは1つのCPU命令では実行されません。
私はLutzに同意しない必要があります...あなたが言及した丸め誤差はPython/Perl / Rubyに存在しますが、Cで実装されている言語とはまったく関係ありません。問題はそれよりも深刻です。
浮動小数点数は、すべてのデータと同様に、最近のコンピューターでは2進数で表されます。循環小数表現の数値(たとえば、1/3 = 0.333333 ...)があるのと同様に、周期小数表現の数値(たとえば、1/10 = 0.0001100110011 ...)もあります。これらの数値は(有限量の)コンピュータメモリでは正確に表すことができないため、これらの数値を含む計算ではエラーが発生します。
これは、ネイティブのバイナリ表現を使用する代わりに、分数の2つの数値(つまり、「分子= 1、分母= 10」)または文字列として数値を表す高精度の数学ライブラリを使用することで回避できます。ただし、他の何かとして格納されている数値の計算を行うために余分な作業が必要になるため、これらのライブラリは、それらを通過する必要のある計算を必然的に遅くします。
Pythonには、整数以外の数値にはいくつかの種類があります。
x = 1 / 2
あなたに標準的なフロートを与えるでしょう。そのタイプはfloat
、本質的にCと同じであり、ハードウェアによって処理されfloat
、世界中の他のすべてと同じ問題を抱えています。
ただし、分数型もあります:
from fractions import Fraction
x = Fraction(1, 2)
これは有理数で正確な算術を持っています。
丸めを実行したいが、コンピューター上の意味のある桁数に満足できない場合、またはプラットフォーム間で異なる可能性があるという事実の場合、10進数タイプが最適です。
from decimal import Decimal
x = Decimal('0.5')
必要に応じて、精度をたとえば100桁に設定できます。または、銀行アプリケーションの場合は2に設定します。
コンピュータが愚かである限り、私たちはおそらくこれほど多くの異なるタイプが必要になるでしょう。少なくとも、Pythonの原則に従って、Pythonでは、数値から必要なものを明示的に選択する必要があります。
さらに、正確な算術演算が丸めの問題を引き起こさないというのは大きな誤解です。正確な値を丸めて、ユーザーにとって有用なことを行うときはいつでも、たとえば、それをユーザーに印刷したり、ユーザーの銀行口座にその金額を追加したりすると、丸めの「奇妙な動作」が発生します。これは、非整数演算に固有のものです。
それはあなたが使用する言語ではなく、あなたがあなたの数をどのように表現するかに依存します。
たとえば、すべてのコードを8051アセンブリで記述しているが、洗練された有理数ライブラリを実装している場合、四捨五入は問題になりません。1/3は1/3に等しいだけです。
ただし、最新の洗練された動的言語を使用していて、IEE754フロートを使用している場合は、IEEE754のすべての制限が適用されます。
生成する数値の詳細を気にする必要がある場合は、それらの表現と、選択したツールによってそれらがどのように操作されるかを理解する必要があります。
アップデート:
PDLは、Perlで科学計算を行うための人気のあるライブラリです。
CPythonとPerlの両方の基礎となるインタープリターはCで実装されているため、Cプログラムのように動作します。
Pythonには、科学計算用のSciPYとNumPyがあります。
外部モジュールを使用して、Python で多精度計算を行うことができます。公式 Web サイトの Multi Precision Math セクションには、それらの多くがリストされています。
そうですね、Rubyの浮動小数点エラーの影響を受けません。例えば:
irb(main):033:0> (2.01 * 1000).to_i
=> 2009
irb(main):034:0> ((2.01 * 1000.0) + 0.5).floor
=> 2010
確かにそうです!
Python 2.6の例:
>>> 1440.0 / 900.0
1.6000000000000001
lutzが言うように、スクリプト言語はCで実装されることが多いため、これらの「機能」を継承します。言語でそれらを補うことは、間違いなく、パフォーマンスまたは移植性におけるある種のトレードオフを意味します。
科学的プログラミングを行う場合、使用するプログラミング言語や数値ライブラリに関係なく、常に丸め誤差について心配する必要があります。
証明: 宇宙の境界近くにある分子の動きを追跡したいとします。宇宙の大きさは、約 930 億光年です (私たちが知る限り)。分子は非常に小さいため、少なくともナノメートルの精度 (10^-6) が必要です。これは 50 桁です。
何らかの理由で、その分子を回転させる必要があります。これにはsin()
、cos()
操作と乗算が含まれます。有効な桁数は単に両方のオペランドの長さの合計であるため、乗算は問題になりません。しかし、どうsin()
ですか?
最終結果に既知の最大誤差が含まれるように、十分な桁数を確保するために誤差方程式を作成する必要があります。この操作を自動的に実行できる「単純な」数値ライブラリを知りません(たとえば、 への呼び出しの一部としてsin()
)。これは、 Matlabなどが必要な場所です。