7

私が受けた最近の宿題では、コンピューターで実行すると精度が失われる可能性のある式を取り上げ、この損失を回避するように変更するように求められました。

残念ながら、これを行うための指示はあまり明確にされていません。実行されているさまざまな例を見て、これを行う特定の方法があることを知っています: テイラー級数を使用する、平方根が含まれる場合は共役を使用する、または 2 つの分数が減算されているときに共通の分母を見つける.

ただし、精度の低下がいつ発生するかを正確に把握するのに苦労しています。これまでのところ、私が確実に知っている唯一のことは、ほぼ同じである 2 つの数値を減算すると、上位の桁が有効であるため精度が失われ、四捨五入によってそれらが失われるということです。

私の質問は、私が探すべき他の一般的な状況は何ですか、そしてそれらにアプローチするための「良い」方法と考えられるものは何ですか?

たとえば、ここに 1 つの問題があります。

f(x) = tan(x) − sin(x)  when x ~ 0

これらの 3 つの選択肢のうち、これを評価するための最良のアルゴリズムと最悪のアルゴリズムは何ですか?

(a) (1/ cos(x) − 1) sin(x),
(b) (x^3)/2
(c) tan(x)*(sin(x)^2)/(cos(x) + 1).

x がゼロに近い場合、tan(x) と sin(x) はほぼ同じになることがわかりました。問題を解決するために、これらのアルゴリズムのいずれかがどのように、またはなぜ優れているか、または劣っているのか理解できません。

4

4 に答える 4

5

通常使用されるもう 1 つの経験則は次のとおりです。長い一連の数値を追加する場合は、ゼロに最も近い数値から追加を開始し、最大の数値で終了します。

これが良い理由を説明するのは少し難しいです。小さな数を大きな数に追加する場合、大きな数の現在の仮数部の最小桁よりも小さいため、それらが完全に破棄される可能性があります。たとえば、次の状況を考えてみましょう。

a = 1,000,000;
do 100,000,000 time:
   a += 0.01;

0.01 が仮数の最小桁より小さい場合、ループは何もせず、最終結果は == 1,000,000 になりますが、これを次のように行うと:

a = 0;
do 100,000,000 time:
   a += 0.01;
a += 1,000,000;

低い数値よりもゆっくりと成長し、正解である == 2,000,000 に近い値になる可能性が高くなります。
これはもちろん極端な例ですが、理解していただければ幸いです。

于 2009-02-02T03:50:57.180 に答える
4

学部生の頃に数学の授業を受けなければならなかったのですが、とても苦痛でした。とにかくIEEE754は、最新の CPU によって通常実装される浮動小数点標準です。基本を理解しておくと、何をしてはいけないかについて多くの直感が得られるため、役に立ちます。簡単に説明すると、コンピューターは浮動小数点数を基数 2 の指数表記のような形式で格納し、指数と仮数の桁数 (ビット) が固定されています。これは、数値の絶対値が大きいほど正確に表現できないことを意味します。IEEE 754 の 32 ビット float の場合、32 ビット float では約 10^38 までの数値を表現できますが、可能なビット パターンの半分は -1 と 1 の間を表します。2^24 (約 1670 万) より大きい値の場合、32 ビット浮動小数点数ではすべての整数を正確に表すことはできません。

これが意味することは、通常、次のことを避けたいということです。

  1. 最終的な答えが小さいと予想されるときに、中間値を大きくする。
  2. 小さな数を大きな数に足したり引いたりする。たとえば、次のように記述したとします。

    for(float index = 17000000; index < 17000001; index++) {}

17,000,000 + 1 は 17,000,000 に切り捨てられるため、このループは終了しません。次のようなものがあった場合:

float foo = 10000000 - 10000000.0001

丸め誤差のため、foo の値は -0.0001 ではなく 0 になります。

于 2009-02-02T03:53:35.740 に答える
2

私の質問は、私が探すべき他の一般的な状況は何ですか、そしてそれらにアプローチするための「良い」方法と考えられるものは何ですか?

精度が大幅に低下したり、壊滅的な損失を被ったりする可能性があるいくつかの方法があります。

最も重要な理由は、浮動小数点数の桁数が限られていることです。たとえば、double は 53 ビットです。つまり、ソリューションの一部ではないが保存する必要がある「役に立たない」数字がある場合、精度が失われます。

例 (デモ用に 10 進型を使用しています):

2.598765000000000000000000000100 -

2.59876500000000000000000000099

興味深いのは、100-99 = 1 の答えです。2.598765 はどちらの場合も等しいので、結果は変わりませんが、8 桁が無駄になります。さらに悪いことに、コンピューターは数字が役に立たないことを認識していないため、数字を保存することを余儀なくされ、その後に 21 個のゼロが詰め込まれ、29 個の数字が無駄になります。残念ながら、違いのためにそれを回避する方法はありませんが、物理学で非常に頻繁に発生する関数である exp(x)-1 など、他のケースがあります。

0 に近い exp 関数はほぼ線形ですが、先頭の桁として 1 を強制します。したがって、有効数字が 12 桁の場合、exp(0.001)-1 = 1.00100050017 - 1 = 1.00050017e-3 となります。

代わりに関数 expm1() を使用する場合は、テイラー級数を使用します。

1 + x +x^2/2 +x^3/6 ... -1 =

x +x^2/2 +x^3/6 =: expm1(x)

expm1(0.001) = 1.00500166667e-3

ずっといい。

2 番目の問題は、pi/2 付近の x の正接のような非常に急な勾配を持つ関数です。tan(11) の勾配は 50000 です。これは、前に丸め誤差によって発生した小さな偏差が 50000 倍に増幅されることを意味します。または、たとえば結果が 0/0 に近づく場合、特異点があります。つまり、任意の値を持つことができます。

どちらの場合も、元の関数を単純化して代替関数を作成します。さまざまなソリューション アプローチを強調しても意味がありません。トレーニングがなければ、そもそも問題を「見る」ことができないからです。

学び、訓練するための非常に優れた本: Forman S. Acton: Real Computing made real

于 2009-12-08T15:04:09.993 に答える
1

避けるべきもう 1 つのことは、ほぼ等しい数値を減算することです。これは、丸め誤差の影響を受けやすくなる可能性があるためです。0 に近い値の場合、cos(x) は 1 に近くなります。したがって、1/cos(x) - 1 は、可能であれば避けたい減算の 1 つです。したがって、(a) は避ける必要があります。 .

于 2009-02-02T04:32:47.143 に答える