2016-12-08 15 views
10

-2 * 4^31 + 1 = -9.223.372.036.854.775.807、ここでは、What range of values can integer types store in C++と言っているように、長期間保存できる最低値を知っています。 だから私は、この操作を持っている:Visual Studioのlong long値

#include <iostream> 

unsigned long long pow(unsigned a, unsigned b) { 
    unsigned long long p = 1; 
    for (unsigned i = 0; i < b; i++) 
     p *= a; 
    return p; 
} 

int main() 
{ 
    long long nr = -pow(4, 31) + 5 -pow(4,31); 
    std::cout << nr << std::endl; 
} 

なぜそれが-9.223.372.036.854.775.808代わりの-9.223.372.036.854.775.803を示していますか?私はVisual Studio 2015を使用しています。

+0

コメントは議論の対象外です。この会話は[チャットに移動]されています(http://chat.stackoverflow.com/rooms/130291/discussion-on-question-by-peter-long-long-value-in-visual-studio)。 –

答えて

14

これは3つの(!)原因がある、本当に厄介な小さな問題です。

浮動小数点演算は近似的に問題があります。コンパイラがfloatまたはdoubleを返すpow関数を選択した場合、4 ** 31は5が1ULP(最小精度の単位)未満になるほど大きいので、何も追加しません(つまり、4.0 ** 31 + 5 == 4.0 ** 31)。 -2で乗算すると損失なしで実行でき、結果はlong longに失われることなく間違った答え-9.223.372.036.854.775.808として保存されます。

第2に、標準ヘッダには、他の標準ヘッダが含まれていてもかまいませんが、必須ではありません。明らかに、Visual Studioのバージョン<iostream>には、(グローバル名前空間でpowが宣言されています)が含まれていますが、Code :: Blocksのバージョンには含まれていません。彼はタイプintの両方の引数4、および31を通過し、宣言された関数は、タイプunsignedの引数を持っているので

第三に、OPのpow機能が選択されていません。 C++ 11以来、多くのオーバーロード(または関数テンプレート)がstd::powです。これらはすべてfloatまたはdoubleを返します(引数のいずれかがタイプlong doubleでない場合 - ここでは適用されません)。

したがって、std::powのオーバーロードは、2つの戻り値でよりよく一致し、浮動小数点の丸めを取得します。

ストーリーのモラル:本当にあなたがやっていることを知っていない限り、標準ライブラリ関数と同じ名前の関数を記述しないでください!

+0

"Visual Studio includes"には、 ""のバージョンには " –

+0

@LThode:Visual Studioに同梱されています。 –

2

最低のlong long値はnumeric_limitsから取得できます。長い長い間、それは次のとおりです。

-9223372036854775808

呼び出されるpow()機能がないので、あなたの観察結果である。その結果

​​

。関数の名前を変更します。

+0

質問の下のコメントをご覧ください。彼はまた、自作の未署名のロングロングファンクションを試みました。 –

3

Visual Studioは、あなたがpow(4U, 31U)を使用しない限り、あなたのpow(unsigned, unsigned)は、両方の引数を変換する必要があり、一方だけ、1つの引数の変換を必要とする、pow(double, int)を定義しています。 C++でのオーバーロードの解決は、結果の型ではなく入力に基づいています。

+0

さらに、 *を含む(必須ではありませんが) 'pow'の標準バージョンを宣言することもできます。私はそれがVSで動作し、Code:Blocksにはないということを疑う。 –

1

powへの最初の呼び出しでは、浮動小数点で動作するC標準ライブラリの関数を使用しています。あなたのpow機能に一意の名前を付けてみてください。

unsigned long long my_pow(unsigned a, unsigned b) { 
    unsigned long long p = 1; 
    for (unsigned i = 0; i < b; i++) 
     p *= a; 
    return p; 
} 

int main() 
{ 
    long long nr = -my_pow(4, 31) + 5 - my_pow(4, 31); 
    std::cout << nr << std::endl; 
} 

このコードは、エラーを報告:「単項マイナス演算子は、結果はまだ符号なし、符号なしの型に適用されます」。基本的に元のコードは浮動小数点関数と呼ばれ、値を否定し、整数演算を適用しました。これは、あなたが探していた答えを与えるのに十分な精度を持っていませんでした(19桁のプレゼンテーションで!)。 、あなたが探している答えを得るに署名を変更するには、次の関数がunsignedを期待するので、他の回答で述べたようにこれはMSVC++ 2013年に私のために働いた

long long my_pow(unsigned a, unsigned b); 

、あなたは浮動小数点powを取得しています符号付き整数定数を受け取ります。整数にUを追加すると、powのバージョンが呼び出されます。

+0

"単項マイナス演算子が符号なしタイプに適用され、結果はまだ符号なし"はエラーではありません。 –

+0

MSVCで持ち上げてください。 :)特別なコンパイルフラグは設定されていませんでした。 – cyberbisson

2

-9.223.372.036.854.775.808結果の唯一の可能な説明は、double値を返す標準ライブラリのpow関数の使用です。その場合、5は二重計算の精度より低くなり、結果は正確に-2 になり、長さが長くなると0x8000000000000000または-9.223.372.036.854.775.808に変換されます。

unsigned long longを返す関数を使用すると、符号なしの型に単項マイナスを適用し、ULLを取得するという警告が表示されます。したがって、演算全体は符号なしlong longとして実行され、符号なしの値としてオーバーフローなしで0x8000000000000005として与える必要があります。符号付きの値にキャストすると、結果は未定義ですが、私が知っているすべてのコンパイラは、同じ表現で符号付き整数を使用するだけです(-9.223.372.036.854.775.803)。

しかし、単に使用して、任意の警告なしに、長い長い署名したとして、計算を行うことが簡単になります:

long long nr = -1 * pow(4, 31) + 5 - pow(4,31); 

結果は完璧ごとに定義されるように加えて、あなたはここにもない未定義のキャストもオーバーフローを持っていますsigned unsigned long longは少なくとも64ビットです。

関連する問題