1. 序章

Javaには、整数演算を実行するためのintlongなどのプリミティブが用意されています。 ただし、場合によっては、数値を格納する必要があります。これにより、これらのデータ型で使用可能な制限がオーバーフローします。

このチュートリアルでは、BigIntegerクラスについて詳しく見ていきます。 ソースコードを調べてその構造を確認し、質問に答えます– h 使用可能なプリミティブデータ型の制限外に多数を格納することは可能ですか?

2. BigIntegerクラス

ご存知のように、 BigIntegerクラスは、プリミティブlongタイプよりも大きい非常に大きな整数計算を含む数学演算に使用されます。 は、不変の任意精度の整数を表します。

先に進む前に、Javaでは、すべてのバイトがビッグエンディアン表記を使用して2の補数システムでで表されることを思い出してください。 ワードの最上位バイトを最小のメモリアドレス(最小のインデックス)に格納します。 さらに、バイトの最初のビットも符号ビットです。 バイト値の例を調べてみましょう。

  • 10000000-128を表します
  • 01111111は127を表します
  • 11111111-1を表します

それでは、ソースコードをチェックして、使用可能なプリミティブの制限を超える特定の数をどのように格納するかを説明しましょう。

2.1. int signum

signum プロパティは、BigIntegerの符号を決定します。 3つの整数値は、値の符号を表します。負の場合は -1 、ゼロの場合は 0 、正の数の場合は1です。

assertEquals(1, BigInteger.TEN.signum());
assertEquals(-1, BigInteger.TEN.negate().signum());
assertEquals(0, BigInteger.ZERO.signum());

BigInteger.ZERO は、大きさの配列のために0の符号を持っている必要があることに注意してください。 この値により、BigInteger値ごとに正確に1つの表現が存在することが保証されます。

2.2. int [] mag

BigInteger クラスのすべての魔法は、magプロパティから始まります。 は、バイナリ表現を使用して、指定された値を配列に格納します。これにより、プリミティブデータ型の制限を省略できます。

さらに、 BigInteger は、それらを32ビット部分 –4バイトのセットにグループ化します。 このため、クラス定義内の大きさはint配列として宣言されます。

int[] mag;

この配列は、指定された値の大きさをビッグエンディアン表記で保持します。 この配列の0番目の要素は、大きさの最も重要なintです。 BigInteger(byte [] bytes)を使用して確認してみましょう。

assertEquals(new BigInteger("1"), new BigInteger(new byte[]{0b1}))
assertEquals(new BigInteger("2"), new BigInteger(new byte[]{0b10}))
assertEquals(new BigInteger("4"), new BigInteger(new byte[]{0b100}))

このコンストラクターは、2の補数のバイナリ表現を含む特定のバイト配列を値に変換します。

符号の大きさの変数( signum )があるため、最初のビットを値の符号ビットとして使用しません。 すぐに確認しましょう:

byte[] bytes = { -128 }; // 1000 0000
assertEquals(new BigInteger("128"), new BigInteger(1, bytes));
assertEquals(new BigInteger("-128"), new BigInteger(-1, bytes));

BigInteger(int signum、byte [] Magnitude)コンストラクターを使用して2つの異なる値を作成しました。 符号の大きさの表現をBigIntegerに変換します。同じバイト配列を再利用し、符号値のみを変更しました。

toString(int radix)メソッドを使用して大きさを出力することもできます。

assertEquals("10000000", new BigInteger(1, bytes));
assertEquals("-10000000", new BigInteger(-1, bytes));

負の値の場合、マイナス記号が追加されることに注意してください。

最後に、マグニチュードの最も重要なintは非ゼロでなければなりません。 これは、BigInteger.ZEROが長さゼロのmag配列を持っていることを意味します。

assertEquals(0, BigInteger.ZERO.bitCount()); 
assertEquals(BigInteger.ZERO, new BigInteger(0, new byte[]{}));

今のところ、他のプロパティの検査はスキップします。 これらは冗長性のために非推奨としてマークされ、内部キャッシュとしてのみ使用されます。

次に、より複雑な例に直接進み、BigIntegerがプリミティブデータ型に数値を格納する方法を確認しましょう。

3. BigIntegerより大きいLong.MAX_VALUE。

すでに知っているように、longデータ型は64ビットの2の補数整数です。 署名されたlongの最小値は-2です。 63 (1000 0000…0000) と最大値2 63 -1 (0111 1111…1111)。 これらの制限を超える数を作成するには、 BigInteger クラス。

次に、 Long.MAX_VALUE より1大きい、2 63に等しい値を作成しましょう。 前の章の情報によると、次のものが必要です。

  • signumプロパティを1に設定
  • mag 配列、合計64ビット、最上位ビットのみが設定されます(1000 0000…0000)。

まず、 setBit(int n)関数を使用してBigIntegerを作成しましょう。

BigInteger bi1 = BigInteger.ZERO.setBit(63);
String str = bi1.toString(2);
assertEquals(64, bi1.bitLength());
assertEquals(1, bi1.signum());
assertEquals("9223372036854775808", bi1.toString());
assertEquals(BigInteger.ONE, bi1.substract(BigInteger.valueOf(Long.MAX_VALUE)));

assertEquals(64, str.length());
assertTrue(str.matches("^10{63}$")); // 1000 0000 ... 0000

バイナリ表現システムでは、ビットは0から始まる右から左に順序付けられていることに注意してください。 BigInteger.ZERO には空のマグニチュード配列がありますが、63番目のビットを設定すると、同時に最も重要な64長配列の0番目の要素になります。 signumは自動的に1に設定されます。

一方、同じビットシーケンスはLong.MIN_VALUEで表されます。 この定数をbyte[] 配列に変換し、 BigInteger:コンストラクトを作成してみましょう。

byte[] bytes = ByteBuffer.allocate(Long.BYTES).putLong(Long.MIN_VALUE).array();
BigInteger bi2 = new BigInteger(1, bytes);
assertEquals(bi1, bi2);
...

ご覧のとおり、両方の値が等しいため、同じアサーションのパックが適用されます。

最後に、内部 int [] mag変数を検査できます。 現在、Javaはこの値を取得するためのAPIを提供していませんが、デバッガーの評価ツールで取得できます。

2つの整数、32ビットの2つのパックを使用して、値を配列に格納します。 0番目の要素はInteger.MIN_VALUEに等しく、もう1つはゼロです。

4. 結論

このクイックチュートリアルでは、BigIntegerクラスの実装の詳細に焦点を当てました。 まず、数値、プリミティブ、およびバイナリ表現ルールに関するいくつかの情報を思い出させることから始めました。

次に、BigIntegerのソースコードを調べました。signummagのプロパティを確認しました。 また、 BigInteger が指定された値を格納する方法を学習し、使用可能なプリミティブデータ型よりも大きな数を提供できるようにしました。

いつものように、GitHubですべてのコードスニペットとテストを見つけることができます。