1の補数とは何か?基本の性質と加算ルール、ゼロの二重性とネットワークでの活用を徹底解説

1の補数とは何か(概要)

1の補数(いちのほすう、one's complement)は、符号付き整数を2進数で表現する方式の一つです。正の数は通常どおりの2進表現を使い、負の数はその正の数の各ビットを反転(0→1、1→0)することで表します。ビット反転(ビットごとのNOT)が負の数の表現に相当します。

基本的な性質

  • nビットでの表現範囲は、+(0 から)2^(n-1)−1 と −(−(2^(n-1)−1) から)まで(例:4ビットなら +0〜+7 と −0〜−7)。

  • “0” が2種類存在する(正のゼロ:すべて0、負のゼロ:すべて1)。

  • 負数の表現はビット単位の反転(NOT)。例えば 8ビットで +5 = 00000101 → −5 = 11111010。

  • 加算の際、最上位からの繰り上がり(キャリー)が発生したらそれを結果に巻き戻して(end-around carry)加える必要がある。

具体例(4ビット)

4ビット表現の例で確認します。

  • 正の数: +3 = 0011

  • 負の数: −3 = 1100(0011 の各ビットを反転)

  • 正の最大: +7 = 0111。対応する負の最小: −7 = 1000(0111 を反転)。

  • 正のゼロ: 0000、負のゼロ: 1111(0000 を反転)。

算術(加算)のルールと例 — end-around carry

1の補数での加算は、まず通常の2進加算を行い、もし最上位桁で繰り上がり(キャリー)が出たら、そのキャリーを最下位桁に戻して(巻き戻して)加えます。これが「end-around carry(キャリーの巻き戻し)」です。

例:4ビットで +3(0011) + (−2)(−2 = 1101)を計算します。

  0011  (+3)
+ 1101  (−2)
-------
 10000  (通常の2進加算の結果、5ビットで10000)

最上位のキャリー1を下位に巻き戻して加えます:

 0000
+    1  (巻き戻し)
-------
 0001  → +1(正しい結果:3 + (−2) = 1)

別例:+5(0101) + +4(0100) = 1001(4ビット)。1001 は負数を表す(−6)なので、オーバーフローが発生したと見ることもできます。1の補数でもオーバーフロー判定や飽和の扱いは設計に依存しますが、上のように巻き戻しが必要かどうかの判定は重要です。

ゼロが二つあることの影響

1の補数では +0(全0)と −0(全1)が存在します。論理的には同一の数値(0)を意味しますが、ビット列が異なるため等価比較で特別扱いが必要になる場合があります。たとえば、算術演算や比較の実装で負のゼロを正のゼロに正規化(normalize)することがよく行われます。

2の補数や符号付き絶対値(符号-絶対値)との比較

主に比較されるのは2の補数方式(two's complement)と符号-絶対値(sign-magnitude)方式です。

  • 2の補数は負数を「各ビットを反転して1を足す」形式で表します。メリットは算術(特に加算)でキャリーの巻き戻しが不要なこと、ゼロが一つしかないこと、符号処理がより単純であることから現代のほとんどのプロセッサが採用しています。

  • 符号-絶対値は符号ビットと残りのビットで絶対値を表す(例:最上位ビットが1なら負)。人間にとって直感的ですが、加算処理が複雑になります。

  • 1の補数はビット反転で負にできるため実装が単純な点と、正負の範囲が対称(ただしゼロが二つ)という特徴があります。しかし加算時のend-around carryや二つのゼロの扱いが問題となり、最終的に多くの設計では2の補数へ移行しました。

数値レンジの公式

nビットの1の補数で表せる整数の範囲は次の通りです。

  • 最大正数: 2^(n-1) − 1

  • 最小負数: −(2^(n-1) − 1)

  • 表現できる値の個数は2^n(ただし0が2つあるため実質的な異なる値は2^n − 1)。

演算アルゴリズム(擬似コード)

簡単な演算や変換の手順を示します。

  • 正数 x を 1の補数表現(nビット)にする:そのまま2進表現(上位ビットは符号含む)

  • 負数 −x を 1の補数表現にする:正の x を nビットで表したあと、各ビットを反転する

  • 2つの1の補数数 A, B を加える(nビット):

    1. 通常の2進加算を行い、nビットを超えるキャリーが出たか確認する。
    2. キャリーがあればそのキャリーを結果の最下位に加える(end-around carry)。
    3. 必要なら結果を正規化して負のゼロを正のゼロにする。
  • 符号の反転(negation)は単にビット反転(NOT)を行うだけ。

実世界での利用例・歴史的背景

1の補数は初期のコンピュータや一部の機種で使われていました。歴史的に見れば、実装が比較的単純だったため採用されたケースがありますが、最終的には2の補数が主流になりました。

また、ネットワーク分野の一部(特に古典的なインターネットチェックサム)では、1の補数和(1's complement addition)が利用されます。IPv4/UDP/TCP のチェックサムや ICMP などで使われる手法は、16ビットワードの1の補数加算をベースにしています(RFC 1071 などで説明されています)。この用途ではビット反転自体が負数表現を扱う目的ではなく、誤り検出のための和の計算方式として用いられています。

実装上の注意点と問題点

  • 二つのゼロ(+0 と −0)をどう扱うか(比較や等価判定での正規化が必要な場合がある)。

  • 加算時の end-around carry を忘れると誤った結果になる。ハードウェア設計では巻き戻しロジックが追加で必要。

  • 桁あふれ(オーバーフロー)の判定や符号判定が2の補数ほど単純でない場合がある。

  • 現代のCPUでは2の補数が標準のため、1の補数データを扱うときは変換や特殊処理が必要になることが多い。

1の補数とチェックサム(ネットワーク)の関係

インターネットのチェックサムアルゴリズムは、複数の16ビットワードを足し合わせてそのビット反転を取る方式が基本です(正確には1の補数加算を用い、最終的な合計の1の補数をチェック値とする)。このため、バイナリデータを16ビット単位で1の補数和する実装を行うことがあります。RFC 1071(Computing the Internet Checksum)がこのアルゴリズムを説明しています。

具体的な変換例(10進数 ⇄ 1の補数)

例:8ビットで +18 を 1の補数表現にし、それを −18 に変換する場合。

+18 = 00010010 (8ビット)
−18 = ビット反転 → 11101101

逆も同様に、11101101 を見て「負の数」であると解釈し、反転して絶対値を得る(11101101 の反転 → 00010010 = 18)。負号は別に保持されないので、ビット列を見て最上位が1なら負と判断し、必要あれば反転して絶対値を出す。

まとめ(いつ使うべきか)

  • 学術的・教育的には、「符号表現の一つ」として重要で、符号表現の違い(2の補数や符号-絶対値)を理解する際にわかりやすい教材になります。

  • 実務的には、現在のCPU設計ではほとんどの場合2の補数が採用されているため、一般的な整数演算に1の補数を選ぶ理由は少ないです。ただし、ネットワークのチェックサムなど、特定の用途では1の補数加算の特性が使われています。

参考文献