0

以前に私は解決した何かを思いついたが、後で私が行っていたものの同様の例を見てみましょう:

int b = 35000000; //35million
int a = 30000000;
unsigned long n = ( 100 * a ) / b;

出力:4294967260

に変更aしただけで、は符号付き32ビット整数であるunsigned longため、正しい85%の出力が表示されます。aしかし、これは後で私を手に入れました。単に計算が行われているa間は値の割り当てはなく、オーバーフローの代わりに30億という正しい値が表示されるはずです。( 100 * a )実際に割り当てがなかったかどうかを理解するために、コードからa削除aし、代わりに手動で値を書き込みます。

int b = 35000000;
unsigned long n = ( 100 * 30000000 ) / b;

大きな驚きは、出力も4294967260であるということでした。
もちろん、30億の値をに割り当てることができますunsigned long。それがオーバーフローの原因だと最初に思ったのです( 100 * 30000000 )が、「何にオーバーフロー?オーバーフローするものは何もない」と聞いてみました。次にb、unsigned longに変更しました。これは、最も驚くべきことに、出力が85%正しかったことです。

最初の例ではaunsigned long

int b = 35000000;
unsigned long a = 30000000;
unsigned long n = ( 100 * a ) / b;

そのままbにしintておくと機能しますが、2番目の例では機能しません。何が起こっているのでしょうか。

これは、うまくいくものとうまくいかないものですべての例を書き直させてもらうには少し圧倒されるかもしれません。

動作(出力= 85):

int b = 35000000;
unsigned long a = 30000000;
unsigned long n = ( 100 * a ) / b;

動作(出力= 85):

unsigned long b= 35000000;
unsigned long n = ( 100 * 30000000 ) / b;

動作しない(オーバーフロー):

int b = 35000000;
int a = 30000000;
unsigned long n = ( 100 * a ) / b;

動作しない(オーバーフロー):

int b = 35000000;
unsigned long n = ( 100 * 30000000 ) / b;
4

5 に答える 5

3

ここで何が起こっているのか説明させてください。
の上:

int b= 35000000;
unsigned long n = ( 100 * 30000000 ) / b;

( 100 * 30000000 ) But on:でオーバーフローが発生するため、値が正しくありません。

unsigned long b= 35000000;
unsigned long n = ( 100 * 30000000 ) / b;

値は正しいので、何が起こっているのでしょうか。

最初の例bは、intTonyが言ったように、の一時値が割り当てられるレジスタが32ビットの符号付き整数を保持できるためにオーバーフローが発生します。これは、100がintであり、 30000000( 100 * 30000000 )もintであり、また、int、この場合のレジスタはスマートです。右側のすべての値が値である必要があると想定している場合、強大な人がパーティーに来ると、aで除算するのは間違っていることがわかります。の値をに格納します。bintintunsigned longintunsigned long/ b( 100 * 30000000 )unsigned long

于 2012-09-19T03:26:47.873 に答える
2

C ++には、「リテラル定数」と呼ばれるプログラミング要素があります。

例(ここから取得):

157//整数定数

0xFE//整数定数

'c'//文字定数

0.2//浮動定数

0.2E-01//浮動定数

"dog"//文字列リテラル

つまり、例に戻ると、 2つのsを100 * 30000000掛け合わせています。intそのため、オーバーフローが発生します。同じタイプのオペランドに対して算術演算を実行すると、同じタイプの結果が得られます。また、スニペットunsigned long a = 30000000;では、整数定数を取得し、それをタイプ30000000の変数に割り当てています。aunsigned long

目的の出力を取得するには、ul末尾に接尾辞を追加しますunsigned long n = ( 100ul * 30000000ul ) / b;

接尾辞の説明があるサイトです。

bが長い署名されていないのに/bがまだ興味深い質問である理由

で除算する前に100 * 30000000実行され、オペランドは両方ともタイプであるためです。bint

于 2012-09-19T03:06:44.320 に答える
1

オーバーフローなしで32ビットの符号付き整数で表すことができる最大数は2147483647です。100*30000000はそれよりも大きくなります。

算術演算のタイプは、それを格納している変数のタイプとは完全に独立しています。これは、オペランドのタイプに基づいています。両方のオペランドがタイプのint場合、結果もタイプにintなり、その結果は変数に格納される前に変換されます。

于 2012-09-19T03:18:29.103 に答える
1

動作(出力= 85):

unsigned long b= 35000000;
unsigned long n = ( 100 * 30000000 ) / b;

ここではなく、以下を使用します。

#include <iostream>

int main() {
    unsigned long b= 35000000;
    unsigned long n = ( 100 * 30000000 ) / b;
    std::cout << n << std::endl;
    return 0;
}

出力は527049830640です(そしてコンパイラーはデフォルトの警告レベルでもオーバーフローについて警告しました)。

重要なのは、Mark Ransom がすでに書いたように、算術演算のタイプはそのオペランドのタイプによって決定されるということです。

定数100intのタイプは、定数30000000のタイプと同様です(32ビット以上intのsを想定するとlong intintが16ビットの場合)。したがって、乗算はタイプintで実行され、32ビットintの場合はオーバーフローします。オーバーフローは未定義の動作ですが、ラップアラウンドはその未定義の動作の最も一般的な兆候であり、結果として値が発生します-1294967296。次に、乗算の結果は、除算の型に変換されますb(これは、符号なしの型であり、Cの用語では、その整数変換ランクはint)の型よりも小さくありません。

符号なし整数型への変換は、2^WIDTHを法とする剰余の削減を意味します。の幅unsigned longが32の場合、その最後の変換の結果は2^32 - 1294967296 = 3000000000であり、商は85になります。しかし、私のシステムのように、の幅unsigned longが64ビットの場合、その変換の結果はです2^64 - 1294967296 = 18446744072414584320

于 2012-09-19T04:20:36.797 に答える
0

もう1つの一般的な解決策は、定数の1つを、操作する前に、より大きな結果タイプに型キャストすることです。誰もが可能なすべての接尾辞を覚えているわけではないので、私はこの方法を好みます。自分も含めて。

この場合、私は以下を使用します:

unsigned long n = ( (unsigned long)100 * 30000000 ) / b;

悲しい部分は、これがアセンブリ言語の1つであるということです。そうです、アセンブリ言語は、C、C ++、および他の多くの言語では正しくありません。Mビット整数にNビット整数を掛けた結果は(M + N )-ビット整数。 (max(MN))-ビット整数ではありません。

編集:マークは興味深い点を指摘しています。コンパイラは、結果の種類を推測するために、結果が格納されている場所を「先読み」しません。したがって、C ++は、部分式の結果自体が決定論的であることを要求します。つまり、100 * 30000000他のコードを見なくても、正確なタイプを常に判別できます。

于 2012-09-19T03:16:50.430 に答える