26

次のコードを検討してください。

#include <iostream>
#include <cmath>

int main() {
    int i = 23;
    int j = 1;
    int base = 10;
    int k = 2;
    i += j * pow(base, k);
    std::cout << i << std::endl;
}

「123」ではなく「122」を出力します。g++ 4.7.2 (MinGW、Windows XP) のバグですか?

4

5 に答える 5

12

std::pow()無限精度を持たない浮動小数点数で動作し、おそらく使用している標準ライブラリの実装は、pow()この無限精度の欠如を関連させる(貧弱な)方法で実装されています。

ただし、整数で動作する独自のバージョンを簡単に定義できます。C++11 では、constexpr(可能であればコンパイル時に結果を計算できるように) 作成することもできます。

constexpr int int_pow(int b, int e)
{
    return (e == 0) ? 1 : b * int_pow(b, e - 1);
}

これが実際のです。


末尾再帰形式 ( Dan Nissenbaumのクレジット):

constexpr int int_pow(int b, int e, int res = 1)
{
    return (e == 0) ? res : int_pow(b, e - 1, b * res);
}
于 2013-04-06T13:47:51.930 に答える
7

これまでの他のすべての回答は、質問の唯一の問題を見逃したり、踊ったりしています。

あなたのpowC++ 実装の は質が悪いです。必要がない場合、不正確な回答を返します。

より良い C++ 実装を入手するか、少なくともその中の数学関数を置き換えてください。Pascal Cuoqが指摘したものは良いです。

于 2013-04-06T21:00:21.027 に答える
3

あなたの問題はgccのバグではありません。それは絶対に確実です。の実装のバグかもしれませんが、あなたpowの問題はpow、不正確な浮動小数点の結果を与えるものを使用しているという事実だけだと思います(これはexp(power * log(base));log(base)は e のべき乗です。

于 2013-04-06T13:51:33.210 に答える
3

少なくとも私のものではありません:

$ g++ --version | head -1
g++ (GCC) 4.7.2 20120921 (Red Hat 4.7.2-2)

$ ./a.out 
123

IDEoneもバージョン 4.7.2 を実行しており、123.


http://www.cplusplus.com/reference/cmath/pow/pow()からの署名

     double pow (      double base,      double exponent );
long double pow ( long double base, long double exponent );
      float pow (       float base,       float exponent );
     double pow (      double base,         int exponent );
long double pow ( long double base,         int exponent );

と を設定する必要がdouble base = 10.0;ありdouble i = 23.0ます。

于 2013-04-06T13:40:54.087 に答える
3

簡単に書くと

#include <iostream>
#include <cmath>

int main() {
    int i = 23;
    int j = 1;
    int base = 10;
    int k = 2;
    i += j * pow(base, k);
    std::cout << i << std::endl;
}

powあなたは何を参照することになっていると思いますか?C++ 標準では、cmath をインクルードした後、グローバル スコープで pow 関数を使用できることさえ保証されていません。

すべてのオーバーロードが少なくともstd名前空間にあることに注意してください。pow整数指数を取るpow関数と、浮動小数点指数を取る関数があります。C++ 実装がグローバル スコープで C pow 関数のみを宣言している可能性は十分にあります。この関数は浮動小数点指数を取ります。問題は、この関数にはいくつかの近似誤差と丸め誤差がある可能性が高いということです。たとえば、その機能を実装する 1 つの可能な方法は次のとおりです。

double pow(double base, double power)
{
    return exp(log(base)*power);
}

pow(10.0,2.0) は、丸め誤差と近似誤差のために 99.99999999992543453265 のような結果になる可能性が十分にあります。浮動小数点から整数への変換によって小数点の前の数値が得られるという事実と組み合わせると、99+3=122 であるため、122 という結果が説明されます。

整数指数を取る pow のオーバーロードを使用したり、float から int への適切な丸めを行ったりしてみてください。整数指数を取るオーバーロードは、10 の 2 乗の正確な結果を与える可能性があります。

編集:

ご指摘のとおり、std::pow(double,int) オーバーロードを使用しようとすると、100 をわずかに下回る値が得られるようです。時間をかけて、ISO 標準と libstdc++ の実装を確認し、C++ から始まることを確認しました。 11 では、欠陥レポート 550を解決した結果、整数の指数を使用するオーバーロードが削除されました。C++0x/C++11 サポートを有効にすると、libstdc++ 実装のオーバーロードが実際に削除されます。これにより、改善が見られなかった理由を説明できます。

とにかく、特に整数への変換が含まれる場合、そのような関数の精度に頼るのはおそらく悪い考えです。浮動小数点値が整数 (100 など) であると予想し、それを int 型の値に変換する場合、ゼロに向かうわずかな誤差は明らかに大きな違いを生みます。したがって、私の提案は、すべての整数を取る独自の pow 関数を作成するか、独自のラウンド関数を使用して double->int 変換に関して特別な注意を払い、ゼロに向かうわずかなエラーが結果を変更しないようにすることです。

于 2013-04-06T13:56:58.553 に答える