私は簡単なプログラムを持っています:
public class Mathz {
static int i = 1;
public static void main(String[] args) {
while (true){
i = i + i;
System.out.println(i);
}
}
}
このプログラムを実行すると、出力にi
の0
だけが表示されます。 i = 1 + 1
、i = 2 + 2
、i = 4 + 4
などが続く最初のラウンドを期待していました。
これは、左側でi
を再宣言しようとするとすぐに、その値が0
にリセットされるという事実によるものですか?
誰かが私にこれのより細かい詳細を教えてくれるなら、それは素晴らしいことです。
int
をlong
に変更すると、期待どおりに数字が出力されるようです。最大32ビットの値に達する速度に驚いています!
この問題は整数オーバーフローが原因です。
32ビットの2の補数演算では:
i
は実際には2のべき乗の値を持ち始めますが、2に達するとオーバーフロー動作が始まります30:
230 + 230 = -231
-231 + -231 = 0
...本質的に算術mod 2 ^ 32であるため、int
算術演算で.
問題は整数オーバーフローです。オーバーフローすると、最小値に戻り、そこから継続します。アンダーフローすると、最大値に戻り、そこから継続します。下の画像は走行距離計です。これを使用してオーバーフローを説明します。これは機械的なオーバーフローですが、それでも良い例です。
走行距離計では、max digit = 9
は最大値を超えているということは9 + 1
を意味し、それは引き継がれて0
を返します。ただし、1
に変更する上位桁がないため、カウンターはzero
にリセットされます。 「整数オーバーフロー」が今思い浮かびます。
Int型の最大10進数リテラルは2147483647(231-1)。 0から2147483647までのすべての10進数リテラルは、intリテラルが現れるどこにでも現れるかもしれませんが、リテラル2147483648は単項否定演算子-のオペランドとしてのみ現れるかもしれません。
整数の加算がオーバーフローした場合、結果は、十分に大きい2の補数形式で表される数学的和の下位ビットになります。オーバーフローが発生した場合、結果の符号は2つのオペランド値の数学的和の符号と同じではありません。
したがって、2147483647 + 1
はオーバーフローし、-2147483648
にラップアラウンドします。したがって、int i=2147483647 + 1
はオーバーフローしますが、これは2147483648
と等しくありません。また、「常に0を出力します」と言います。それは、http://ideone.com/WHrQIWのためです。以下では、これらの8つの数字は、ピボットおよびオーバーフローするポイントを示しています。その後、0の印刷を開始します。また、計算の速さに驚かないでください。今日のマシンは高速です。
268435456
536870912
1073741824
-2147483648
0
0
0
0
いいえ、ゼロのみを出力しません。
これに変更すると、何が起こるかがわかります。
int k = 50;
while (true){
i = i + i;
System.out.println(i);
k--;
if (k<0) break;
}
起こることはオーバーフローと呼ばれます。
static int i = 1;
public static void main(String[] args) throws InterruptedException {
while (true){
i = i + i;
System.out.println(i);
Thread.sleep(100);
}
}
出力:
2
4
8
16
32
64
...
1073741824
-2147483648
0
0
when sum > Integer.MAX_INT then assign i = 0;
i
の値は、一定量の2進数を使用してメモリに保存されます。使用可能な数字よりも多くの数字が必要な場合、最下位の数字のみが保存されます(最上位の数字は失われます)。
i
をそれ自体に追加することは、i
を2倍することと同じです。各桁を左にスライドさせて右にゼロを置くことで、10進表記で数値を10倍するのと同じように、2進表記で数値を2倍する方法も同じように実行できます。これにより、右側に1桁が追加されるため、左側の桁が失われます。
ここで、開始値は1です。したがって、i
(たとえば)を格納するために8桁を使用する場合、
00000001
00000010
00000100
など、最後の非ゼロステップまで
10000000
00000000
数を格納するためにいくつの2進数が割り当てられていても、開始値が何であっても、最終的にすべての桁は左に押し出されるため失われます。その時点の後、数を2倍に増やしても数は変わりません。すべてゼロで表されます。
私は十分な評判を持っていないので、制御された出力を持つCの同じプログラムの出力の画像を投稿することはできません i = 1073741824 + 1073741824 への変更 -2147483648 さらにもう1つ追加すると、intの範囲外になり、 Zero 。
#include<stdio.h>
#include<conio.h>
int main()
{
static int i = 1;
while (true){
i = i + i;
printf("\n%d",i);
_getch();
}
return 0;
}
正しいですが、31回の反復の後、1073741824 + 1073741824は正しく計算されず、その後は0のみが出力されます。
BigIntegerを使用するようにリファクタリングできるため、無限ループが正しく機能します。
public class Mathz {
static BigInteger i = new BigInteger("1");
public static void main(String[] args) {
while (true){
i = i.add(i);
System.out.println(i);
}
}
}
8ビットの数字を使用しますが、これは短いスペースで完全に詳述できるためです。 16進数は0xで始まり、2進数は0bで始まります。
8ビットの符号なし整数の最大値は255(0xFFまたは0b11111111)です。 1を追加すると、通常256(0x100または0b100000000)が得られると予想されます。しかし、これはビット数が多すぎるため(9)、最大値を超えているため、最初の部分はドロップされるだけで、事実上0が残ります(0x(1)00または0b(1)00000000、ただし1がドロップされます)。
したがって、プログラムを実行すると、次のようになります。
1 = 0x01 = 0b1
2 = 0x02 = 0b10
4 = 0x04 = 0b100
8 = 0x08 = 0b1000
16 = 0x10 = 0b10000
32 = 0x20 = 0b100000
64 = 0x40 = 0b1000000
128 = 0x80 = 0b10000000
256 = 0x00 = 0b00000000 (wraps to 0)
0 + 0 = 0 = 0x00 = 0b00000000
0 + 0 = 0 = 0x00 = 0b00000000
0 + 0 = 0 = 0x00 = 0b00000000
...
このような場合のデバッグには、ループ内の反復回数を減らすことをお勧めします。 while(true)
の代わりにこれを使用してください:
for(int r = 0; r<100; r++)
その後、2で始まり、オーバーフローが発生するまで値が2倍になることがわかります。
タイプint
の最大の10進リテラルは 2147483648 (= 231)。からのすべての10進リテラル 0から2147483647 intリテラルが現れる可能性のあるどこにでも現れるかもしれませんが、リテラル 2147483648 単項否定演算子-のオペランドとしてのみ表示されます。
整数の加算がオーバーフローした場合、結果は、十分に大きい2の補数形式で表される数学的和の下位ビットになります。オーバーフローが発生した場合、結果の符号は2つのオペランド値の数学的和の符号と同じではありません。