最終更新日時(UTC):
が更新

履歴 編集

符号付き整数型が2の補数表現であることを規定(C++20)

概要

C++20では、符号付き整数型のビット表現を「2の補数 (Two's Complement)」に規定する。

これによって、2の補数を前提としたビット演算ができるようになる。また、2の補数には値-0を表すビット値が存在しないため、「ビット値が異なる値0と値-0を等値であるとする」ということが起こらなくなる (1の補数や絶対値表現には-0を表すビット値がある)。これにより、符号付き整数型に対するハッシュ値が一意に定まり、strong ordering (強順序、全順序) の要件を満たすようになる (-0 < 0が成り立たなかったが、-0がなくなった)。

-0は、0を意味する。

符号付き整数型に対する右シフトは「符号拡張 (sign extension)」を行い、符号ビットが右に伝播する。

ただし、符号付き整数型のオーバーフロー時の動作は、これまでと変わらず未定義動作である。std::numeric_limits<符号付き整数型>::is_moduloはデフォルトでfalseのままとなる。

ビット値・ビット演算の例

#include <cassert>
#include <cstdint>

int main()
{
  // 符号反転したビット値
  {
    std::int8_t x = 11;
    assert(x == 0b0000'1011);

    std::int8_t y = -x;
    assert(y == (~x + 1)); // 負数は、ビット反転して+1した値
    assert(y == static_cast<std::int8_t>(0b1111'0101));
  }
  // 0と-0は同じビット値
  {
    std::int8_t x = 0;
    std::int8_t y = -0;

    assert(x == static_cast<std::int8_t>(0b0000'0000));
    assert(y == static_cast<std::int8_t>(0b0000'0000));
  }
  // 右シフト時の符号拡張
  {
    std::int8_t x = -124;
    assert(x == static_cast<std::int8_t>(0b1000'0100));

    x >>= 2;

    assert(x == static_cast<std::int8_t>(0b1110'0001));
  }
}

出力

符号付き整数型は一意なオブジェクト表現をもつ

#include <type_traits>
#include <cstdint>

int main()
{
  static_assert(std::has_unique_object_representations<char>::value);
  static_assert(std::has_unique_object_representations<short>::value);
  static_assert(std::has_unique_object_representations<int>::value);
  static_assert(std::has_unique_object_representations<long>::value);
  static_assert(std::has_unique_object_representations<long long>::value);

  static_assert(std::has_unique_object_representations<std::int8_t>::value);
  static_assert(std::has_unique_object_representations<std::int16_t>::value);
  static_assert(std::has_unique_object_representations<std::int32_t>::value);
  static_assert(std::has_unique_object_representations<std::int64_t>::value);
}

出力

三方比較演算子の例

#include <type_traits>
#include <compare>

int main()
{
  int a = 1;
  int b = 2;

  // 符号付き整数型はstrong orderingをもつ
  auto r = a <=> b;
  static_assert(std::is_same_v<decltype(r), std::strong_ordering>);
}

出力

この機能が必要になった背景・経緯

Visual Studio、GCC、Clangといった主要な処理系が、2の補数以外をサポートしていなかった。

C11規格は、2の補数のほかに、1の補数表現 (Ones' complement) と符号ビット付き絶対値表現 (Signed magnitude) を許可しているが、C++では本文書の概要にも記載したように、ハッシュ値の一意性と全順序をサポートするため、2の補数に規定する。

関連項目

参照