6

C/C++ で三角関数の計算を行っていますが、丸め誤差の問題が発生しています。たとえば、私の Linux システムでは次のようになります。

#include <stdio.h>
#include <math.h>

int main(int argc, char *argv[]) {
    printf("%e\n", sin(M_PI));
    return 0;
}

このプログラムは、次の出力を提供します。

1.224647e-16

正解がもちろん0のとき。

三角関数を使用する場合、どの程度の丸め誤差が予想されますか? そのエラーをどのように処理するのが最善ですか? Bruce Dawson のComparing Floating Point Numbersから、浮動小数点数を比較するための Units in Last Place 手法に精通していますが、0 と 1.22e-16 はかなりの数の ULP が離れているため、ここでは機能しないようです。

4

9 に答える 9

16

sin(pi) の答えは 0 だけです - Pi のすべての桁を含めましたか?

-このあたりで皮肉やユーモアのセンスが明らかに欠けていることに気づいた人はいますか?

于 2009-10-06T19:29:42.417 に答える
13

IEEE double は 52 ビットの仮数を格納し、「暗黙の先頭の 1」が 53 ビットの数値を形成します。したがって、結果の下位ビットのエラーは、数値のスケールの約 1/2^53 になります。出力は 1.0 と同じ順序であるため、10^16 のほぼ 1 つの部分になります (53*log(2)/log(10) == 15.9 のため)。

あ、はい。これは、期待できる精度の限界です。あなたが使用している ULP 手法が何であるかはわかりませんが、間違って適用していると思われます。

于 2009-10-06T19:36:38.037 に答える
6

@Josh Kelley - 深刻な答えです。
一般に、float や double を含む演算の結果を相互に比較するべきではありません。

唯一の例外は割り当てです。
float a=10.0;
float b=10.0;
次に a==b

それ以外の場合は、常に bool IsClose(float a,float b, float error) のような関数を記述して、2 つの数値が互いに「エラー」内にあるかどうかを確認できるようにする必要があります。
兆候を確認する/ファブを使用することも忘れないでください--1.224647e-16の可能性があります

于 2009-10-06T19:42:40.113 に答える
1

エラーの原因は 2 つあります。sin() 関数と M_PI の近似値。sin() 関数が「完全」だったとしても、M_PI の値も完全でない限り、0 を返しません。これはそうではありません。

于 2009-10-06T22:05:22.067 に答える
0

プログラムで小数点以下16桁以上の有効数字が必要な場合を除いて、おそらく手動で丸めることができます。私のプログラミングゲームの経験から、私たちは常に小数を許容できる有効数字に丸めました。例えば:

#include <math.h>
#include <stdio.h>
#include <stdlib.h>

#define HALF 0.5
#define GREATER_EQUAL_HALF(X) (X) >= HALF

double const M_PI = 2 * acos(0.0);

double round(double val, unsigned  places = 1) 
{
    val = val * pow(10.0f, (float)places);
    long longval = (long)val;
    if ( GREATER_EQUAL_HALF(val - longval) ) {
       return ceil(val) / pow(10.0f, (float)places);
    } else {
      return floor(val) / pow(10.0f, (float)places);
    }
}

int main() 
{
    printf("\nValue %lf", round(sin(M_PI), 10));
    return 0;
}
于 2009-10-06T20:44:19.447 に答える
0

むしろそれはシステムに依存すると思います。超越関数がどれほど正確になるかについて、標準が言うことは何もないと思います。残念ながら、関数の精度についての議論を見た覚えがないので、おそらく自分で理解する必要があります。

于 2009-10-06T19:36:32.437 に答える
-1

私のシステムでもまったく同じ結果が得られます-十分に近いと思います

フォーマット文字列を「%f\n」に変更することで問題を解決できます:)

ただし、これにより「より良い」結果が得られます。少なくとも私のシステムでは、-3.661369e-245 が得られます。

#include <stdio.h>
#include <math.h>

int main(int argc, char *argv[]) {
    printf("%e\n", (long double)sin(M_PI));
    return 0;
}
于 2009-10-06T19:46:51.870 に答える
-2

たぶん実装の精度が低すぎる

M_PI = 3.14159265358979323846 (M_PI is not π)

http://fresh2refresh.com/c/c-function/c-math-h-library-functions/

これは実装の不正確さです。上記の Andy Ross の回答の下にある Stephen C. Steel のコメントと chux の回答を参照してください。

于 2015-08-15T23:32:18.530 に答える