[Java How To Programming] [Home on 246net] [Home on Alles net]
Powered by SmartDoc

整数型の扱い

TAKAHASHI, Toru
torutk@gmail.com

目次

整数型を扱う

文字列から整数への変換

16進数文字列を整数型へ変換する

16進数文字列から整数値へ変換するメソッドが、数値型のラッパークラスに用意されています。

整数型の各ラッパークラスと文字列から整数への変換メソッド
ラッパークラス decodeメソッド parseXXXメソッド
Byte static Byte decode(String) static byte parseByte(String, int)
Short static Short decode(String) static short parseShort(String, int)
Integer static Integer decode(String) static int parseInt(String, int)
Long static Long decode(String) static long parseLong(String, int)
対応基数および文字列表現形式
メソッド名 対応可能進数 文字列表現形式
decodeメソッド 8 0 を先頭に付与
10 そのまま
16 0xまたは0Xまたは#を先頭に付与
例:"0xCAFEBABE"
parseXXXメソッド 2〜36進数 そのまま
16進の例:"CAFEBABE"

decodeメソッドとparseXXXメソッドの違いは、前者が16進数値文字の前に16進数であることを示す"0x"または"0X"または"#"を付ける必要があることです。

16進数文字列から整数値への変換
  // decode
  byte byteValue = Byte.decode("0x1a");
  short shortValue = Short.decode("0x3AC0");
  int intValue = Integer.decode("0XC3C071");
  long longValue = Long.decode("#13af21ef");
  :
  // parseXXX
  byte byteValue = Byte.parseByte("12", 16);
  short shortValue = Short.parseShort("3AC0", 16);
  int intValue = Integer.parseInt("C3C071", 16);
  long longValue = Long.parseLong("13af21ef", 16);

16進数文字列で表現する値は正の値とみなされるので、それぞれの整数型の最大値を越える値となる場合、例外NumberFormatExceptionが発生します。例えばbyte型の場合、値の有効範囲はByte.MIN_VALUE(-128)〜Byte.MAX_VALUE(127)ですから、16進数文字列で"0x80"(10進数で128)以上のものを指定すると例外となります。

16進数文字列から整数値への変換
  // NumberFormatExceptionが発生
  byte byteValue = Byte.decode("0x80");
  short shortValue = Short.decode("0x8000");
  int intValue = Integer.decode("0x80000000");
  long longValue = Long.decode("0x8000000000000000");
  :
  byte byteValue = Byte.parseByte("80", 16);
  short shortValue = Short.parseShort("8000", 16);
  int intValue = Integer.parseInt("80000000", 16);
  long longValue = Long.parseLong("8000000000000000", 16);

これは結構深刻な問題で、16進数文字列のダンプデータをbyte配列へ読み込む際などに細工が必要となります。

そこで、より大きい整数型で変換し、本来の整数型へキャストで変換する方法で回避します。

16進数文字列から整数値への変換(最大値を越える場合)
  byte byteValue = (byte)Short.decode("0x80");
  short shortValue = (short)Integer.decode("0x8000");
  int intValue = (int)Long.decode("0x80000000");
  :
  byte byteValue = (byte)Short.parseShort("80", 16);
  short shortValue = (short)Integer.parseInt("8000", 16);
  int intValue = (int)Long.parseLong("80000000", 16);
  long longValue = new BigInteger("8000000000000000", 16).longValue();

プリミティブ型およびそのラッパークラスにはlongより大きい整数型はないので、java.math.BigIntegerを代用します。そのため、少々コードの記述が異なります。

整数から文字列への変換

整数値を16進数文字列に変換する

整数を16進数文字列に変換する方法として、以下の2つの方法が思いつきます。

toHexStringメソッドによる変換

IntegerクラスおよびLongクラスに用意されているtoHexStringメソッドを使用します。BigIntegerクラスの場合、toString(int radix)メソッドで基数に16を指定します。

いずれも、16進数を示す識別(例:"0x")は付与されません。

整数値を16進数文字列に変換
  // 整数ラッパークラスを使用
  String hexText1 = Integer.toHexString(intValue);
  String hexText2 = Long.toHexString(longValue);

  // BigIntegerの場合
  BigInteger bigValue = new BigInteger(...);
  String hexText3 = bigValue.toString(16);

formatメソッドによる変換

C言語のprintf関数に慣れているプログラマにはこちらの方法がよいかもしれません。

formatメソッドで整数を16進文字列に変換
  String text1 = String.format("%x", intValue);
  String text2 = String.format("%08x", intValue);
  String text3 = String.format("%#x", intValue);
formatによる16進変換の例
10進数値 %x %08x %#x
31 1f 0000001f 0x1f
257 101 00000101 0x101
65535 ffff 0000ffff 0xffff
  • %#08xのように#と最小桁数を併用しても、%#xと同じ結果となり、最小桁数が有効になりません。

整数値を2進数文字列に変換する

IntegerクラスおよびLongクラスに用意されているtoBinaryStringメソッドを使用します。BigIntegerクラスの場合、toString(int radix)メソッドで基数に2を指定します。

整数値を2進数文字列に変換
  // 整数ラッパークラスを使用
  String binaryText1 = Integer.toBinaryString(intValue);
  String binaryText2 = Long.toBinaryString(longValue);

  // BigIntegerの場合
  BigInteger bigValue = new BigInteger(...);
  String hexText3 = bigValue.toString(2);

ビット操作

ビット演算

整数型(char含む)のビット演算
演算子 演算内容
& ビット同士のAND (論理積)
| ビット同士のOR (論理和)
^ ビット同士のXOR (排他的論理和)
~ ビット同士のNOT (反転)
<< 左シフト、右側はゼロ埋め
>> 右シフト、左側は符号ビット埋め
>>> 右シフト、左側はゼロ埋め

ビット問い合わせ

整数値のbit問い合わせ
問い合わせ内容 Integer Long
2の補数2進数表現で1となっているビット数 Integer.bitCount(intValue) Long.bitCount(longValue)
valueの1となっている最も上位ビットと同じ位置だけ1となり残りは0となっている値 Integer.highestOneBit(intValue) Long.highestOneBit(longValue)
valueの1となっている最も下位ビットと同じ位置だけ1となり残りは0となっている値 Integer.lowestOneBit(intValue) Long.lowestOneBit(longValue)
valueの1となっている最も上位ビットの前(左)にある0となっているビット数 Integer.numberOfLeadingZeros(intValue) Long.numberOfLeadingZeros(longValue)
valueの1となっている最も下位ビットの後(右)にある0となっているビット数 Integer.numberOfTrailingZeros(intValue) Long.numberOfTrailingZeros(longValue)
valueのビット並びの逆順 Integer.reverse(intValue) Long.reverse(longValue)
valueのビット並びを左に回転、左側で飛び出たビットは右側最下位ビットに入れる Integer.rotateLeft(intValue, dist) Long.rotateLeft(longValue, dist)
valueのビット並びを右に回転、右側で飛び出たビットは左側最上位ビットに入れる Integer.rotateRight(intValue, dist) Long.rotateRight(longValue, dist)

ビットフィールド

ネットワーク通信やファイルフォーマットにおいて、バイナリデータを扱う場合にしばしば用いられているのが「ビットフィールド」です。

以下、64bit (long型)の整数値のうち50bit〜38bitの13bit領域に、整数を格納するビットフィールドを読み書きするコード例を紹介します。このビットフィールドは13bitなので、0〜16383の値を表現できます。

63     50     38                 0
+------+-------+-----------------+
|      |       |                 |
+------+-------+-----------------+

このビットフィールドを読み書きする場合は、2つの定数ビットマスクおよびシフト量を定義して、ビット演算を行います。

ビットマスクは、ビットフィールドの該当ビット部分を1、その他を0としたビット列で表わします。上記の例では、64bitのうち、50bit〜38bitが1、それ以外を0とした値となります。

63     56 55    48 47    40 39    32 31    24 23    16 15     8 7      0
+--------+--------+--------+--------+--------+--------+--------+--------+
|00000000|00000111|11111111|11000000|00000000|00000000|00000000|00000000|
+--------+--------+--------+--------+--------+--------+--------+--------+
-> 0x0007ffc000000000

ビットフィールドに符号なし整数を格納

ビットフィールドの読み書き(符号なし)
  private static final long THE_MASK = 0x0007ffc000000000L;
  private static final int  THE_SHIFT = 38;

  :
  // 64bit値からのビットフィールド読み出し
  long code = 0x123456789abcdef0L;
  int value = (int)( (code & THE_MASK) >>> THE_SHIFT );

  // 64bit値へのビットフィールド書き込み
  int value = 0x1234;
  code = (code & ~THE_MASK) | ((long)value << THE_SHIFT & THE_MASK);

符号なし整数の場合、シフト演算では最上位ビットの値にかかわらず0で埋めるため、>>>演算子を使用します。

ビットフィールドに符号付整数を格納

ビットフィールドの読み書き(符号付)
  private static final long THE_MASK = 0x0007ffc000000000L;
  private static final int  THE_SHIFT = 38;

  :
  // 64bit値からのビットフィールド読み出し
  long code = 0x123456789abcdef0L;
  long raw = code & THE_MASK;
  if ((raw & Long.highestOneBit(THE_MASK)) != 0) {
      raw |= ~THE_MASK;
  }
  int value = (int)(raw >> THE_SHIFT);

  // 64bit値へのビットフィールド書き込み
  int value = -1;
  code = (code & ~THE_MASK) | ((long)value << THE_SHIFT & THE_MASK);

符号付き整数の場合、最上位ビットが1のときが負の値を示します。この例ではビットフィールドに13bit幅を使用しているので、13bit目(64bit中の50bit目)が符号ビットとなります。

しかし、ビットマスクを使用して抜き出した値(上記コードではlong型のraw変数)は、13bit幅ではなく64bit幅の整数なので、負の値とするために51bit目〜63bit目までを1で埋める処理を行っています。なお、副作用で37bit目〜0bit目も1となっていますが、これは次のシフト演算で消えるので無視しても支障ありません。

63     56 55    48 47    40 39    32 31    24 23    16 15     8 7      0
+--------+--------+--------+--------+--------+--------+--------+--------+
|00010010|00110100|01010110|01111000|10011010|10111100|11011110|11110000|
+--------+--------+--------+--------+--------+--------+--------+--------+
 -> 0x123456789abcdef0

○ THE_MASK(0x0007ffc000000000)とのビット論理積

63     56 55    48 47    40 39    32 31    24 23    16 15     8 7      0
+--------+--------+--------+--------+--------+--------+--------+--------+
|00000000|00000100|01010110|01000000|00000000|00000000|00000000|00000000|
+--------+--------+--------+--------+--------+--------+--------+--------+
-> 0x0004566000000000

○ 符号ビット(50bit目)が1の場合に、 raw |= ~THE_MASK

63     56 55    48 47    40 39    32 31    24 23    16 15     8 7      0
+--------+--------+--------+--------+--------+--------+--------+--------+
|11111111|11111100|01010110|01111111|11111111|11111111|11111111|11111111|
+--------+--------+--------+--------+--------+--------+--------+--------+
-> 0xfffc567fffffffff

○ THE_SHIFT分右算術シフト演算
63     56 55    48 47    40 39    32 31    24 23    16 15     8 7      0
+--------+--------+--------+--------+--------+--------+--------+--------+
|11111111|11111111|11111111|11111111|11111111|11111111|11110001|01011001|
+--------+--------+--------+--------+--------+--------+--------+--------+
-> 0xfffffffffffff159 (-3751)

byte配列から整数値への変換

バイナリ・データをJava以外のプログラムとやりとりする場合などで、バイト配列から整数値を生成することが必要になることがあります。

  1. BytetArrayInputStreamを使用する
  2. ByteBufferを使用する
  3. 自前で処理する

ByteArrayInputStreamを使用する

まずは、JavaのI/OストリームAPIを使用してバイトデータから整数へ変換するコードを以下に示します。

バイト配列の並びはビッグエンディアンを前提としています。

byte[]から整数値へByteArrayInputStreamを使用して変換
  byte[] data = { (byte)0x87, 0x65, 0x43, 0x21 };  // (1)
  ByteArrrayInputStream bais = new ByteArrayInputStream(data); // (2)
  DataInputStream dis = new DataInputStream(bais); // (3)
  try {
      int value = dis.readInt(); // (4)
  } catch (IOException e) {
      // ToDo: エラー処理
  }

(1)バイト配列を生成するときに、byte型は-128〜+127の範囲であるため、128(0x80)以上の値を代入するには(byte)型へのキャストを明示的に記述する必要があります。

(2) I/OストリームのAPIには、メモリ上にあるバイト配列を入出力先として使うクラスが用意されています。

(3)整数型の読み書きはI/OストリームではDataInputStream/DataOutputStreamを使用します。

(4)整数(int)型を読み込むには、DataInputStreamクラスのreadIntメソッドを使用します。なお、DataInputStreamでは、データがビッグエンディアンの並び順となっていることを前提としています。

ByteBufferを使用する

Java NIO APIを使用して、バイトデータから整数へ変換するコードを以下に示します。

以下はバイト配列の並びをデフォルトのビッグエンディアンで使用しています。リトルエンディアンとして処理することもできます。

byte[]から符号有整数値へByteBufferを使用して変換(ビッグエンディアン)
  byte[] data = { (byte)0x87, 0x65, 0x43, 0x21 };
  ByteBuffer buffer = ByteBuffer.wrap(data);
  int value = buffer.getInt();

上記のコードでint型の変数valueには、0x87654321が入ります。

byte[]から符号無整数値へByteBufferを使用して変換(ビッグエンディアン)
  byte[] data = { (byte)0x87, 0x65, 0x43, 0x21 };
  ByteBuffer buffer = ByteBuffer.wrap(data);
  long value = buffer.getInt() & 0xffff_ffffL;
byte[]から符号有整数値へByteBufferを使用して変換(リトルエンディアン)
  byte[] data = { (byte)0x87, 0x65, 0x43, 0x21 };
  ByteBuffer buffer = ByteBuffer.allocate(4);
  buffer.order(ByteOrder.LITTLE_ENDIAN);
  buffer.put(data);
  buffer.flip();
  int value = buffer.getInt();

上記のコードでint型の変数valueには、0x21436587が入ります。

自前で処理する

byte[]から符号なし整数値に変換
byte[] buf = ...;
short u8 = buf[0] & 0xff;
int u16 = (buf[0] & 0xff) << 8 | (buf[1] & 0xff);
long u32 = (buf[0] & 0xff) << 24 | (buf[1] & 0xff) << 16 | (buf[2] & 0xff) << 8
                                 | (buf[3] & 0xff;
byte[]から符号あり整数値に変換
byte[] buf = ...;
byte s8 = buf[0];
short s16 = buf[0] << 8 | (buf[1] & 0xff);
int s32 = buf[0]  << 24 | (buf[1] & 0xff) << 16 | (buf[2] & 0xff) << 8
                        | (buf[3] & 0xff;