最近パック数値なるものを扱う機会があったので、忘れないようにメモ。
パック数値とは?
- 1バイトの数字を4ビットで表現する数値のこと。ゾーン数値と比べて単純計算ではあるが、使用容量が半分ですむ。
- パック数値には、符号なしパックと符号ありパックが存在する。
- 符号のあらわし方は、末尾の4ビットで表現する。符号なしの場合でも、末尾4ビットで符号なしであることを設定する必要あり。
- 末尾4ビットが必ず符号領域となるため、ゾーン数値で表した場合の桁数は奇数となる。
【ゾーン数値とパック数値の比較】
| 10進数数値 | ゾーン数値(JISの場合) | 符号なしパック | 符号ありパック | 
| 12345 | 0x3132333435 (5バイト) | 0x12345F (3バイト) | 0x12345C (3バイト) | 
| +12345 | 0x31323334C5 (5バイト) | - | 0x12345C (3バイト) | 
| -12345 | 0x31323334D5 (5バイト) | - | 0x12345D (3バイト) | 
パック数値をアンパックするソース
Javaの場合
// 呼び出し例
// unpack(new byte[]{0x01, 0x2F});  -> 12
// unpack(new byte[]{0x00, 0x00, 0x33, 0x44, 0x0D}); -> -33440
public static long unpack(byte[] pack) {
    long num = 0;
    int weight = 1;
    for (int i = pack.length - 1; i >= 0; i--) {
        num += (pack[i] >> 4) * weight;
        weight *= 10;
        if (i != 0) {
            num += (pack[i - 1] & 0x0F) * weight;
            weight *= 10;
        }
    }
    byte sign= (byte) (pack[pack.length - 1] & 0x0F);
    if (sign == 0x0F || sign == 0x0C) {
        // nop
    } else if (sign == 0x0D) {
        num *= -1;
    } else {
        throw new IllegalArgumentException("invalid sign");
    }
    return num;
}
ruby
def self.un_pack(pack)
  sprintf("%X", pack).gsub(/^(.+)(.)$/) {|str|
    case $2
      when "F", "C"
        ""
      when "D"
        "-"
      else
        raise RuntimeError.new("illegal sign. sign = [#{$2}]")
    end + $1
  }.to_i
end
