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

履歴 編集

type-alias
<stdfloat>

std::float16_t(C++23)

namespace std {
#if defined(__STDCPP_FLOAT16_T__)
  using float16_t = implementation-defined;
#endif
}

概要

16ビット半精度の浮動小数点数型。

内部表現

この型は、ISO/IEC/IEEE 60559 (IEEE 754) 浮動小数点数規格のbinary16フォーマットをもつ。

符号ビット数 指数ビット数 仮数ビット数 最大指数
1 5 10 15

リテラル

値にサフィックスとしてf16もしくはF16を指定することで、std::float16_tのリテラルとすることができる。

std::float16_t a = 1.0f16;
std::float16_t b = 2.0F16;

事前定義マクロ

  • この型は、事前定義マクロ__STDCPP_FLOAT16_T__が定義されない場合、定義されない
    • ISO/IEC/IEEE 60559 (IEEE 754) のbinary16フォーマットが実装される環境でこのマクロは定義される

順位

浮動小数点数の変換で使用される順位 (rank) は、以下のように定義される:

注意として、浮動小数点数型T1の値集合が浮動小数点数型T2の値集合の部分集合でも上位集合でもない場合、浮動小数点数型T1T2の変換順位は順位通りではない。これは、一方の型が他方より大きな範囲と低い精度の両方を持つ場合に起こり得る。

順位が同じ浮動小数点数型は、サブ順位 (subrank) で順序付けられる。拡張浮動小数点数は、標準浮動小数点数型よりも大きなサブ順位をもつ。

昇格と変換

C言語の名残と後方互換性のために、オーバーロード解決でのfloatからdoubleへの変換は昇格と見なされるが、ほかの浮動小数点数型では昇格はない。

2つの浮動小数点型の少なくとも一方が拡張浮動小数点型である場合、変換先の型が変換元の型より大きいか等しい場合にのみ、2つの浮動小数点型間の変換が暗黙的に行われる。いかなる暗黙の変換も損失なく、値を正確に保持する。潜在的に損失のある変換はすべて明示的でなければならない。

小さい精度への変換は、(){}による直接初期化、もしくは明示的なキャストのみ許可される。

void f(std::float16_t);
void g() {
  std::float16_t a = 1.0; // エラー!小さい精度への暗黙変換はできない
  std::float16_t b(2.0);  // OK : 直接初期化
  std::float16_t c{3.0};  // OK : 直接初期化
  a = 4.0;                // エラー!小さい精度への暗黙変換はできない
  f(5.0);                 // エラー!小さい精度への暗黙変換はできない
  f(static_cast<std::float16_t>(5.0)); // OK : 明示的なキャスト
}

通常の算術変換

算術演算での型変換は、以下のような規則で行われる。

  • どちらかのオペランドが浮動小数点数型である場合、
    • 両方のオペランドが同じ型であれば、型変換は行われない
    • そうでなく、オペランドの一方が浮動小数点数型でなければ、浮動小数点数型のオペランドの型に変換される
    • そうでなく、オペランドの浮動小数点数型の変換順位が等しくない場合、小さい方の順位をもつ浮動小数点数型は、もう一方の浮動小数点数型に変換される
      • float + doubledoubleに、std::float16_t + floatfloatに変換される
    • そうでなく、変換順位が等しい場合、サブ順位で比較が行われ、より大きいサブ順位をもつ浮動小数点数型に変換される
      • float + std::float32_tstd::float32_tに変換される
    • そうでなければ、式は不適格となる
      • std::float16_t + std::bfloat16_t不適格

#include <stdfloat>
#include <type_traits>

int main() {
  float f32 = 1.0;
  std::float16_t f16{2.0};
  std::bfloat16_t b16{3.0};
  std::float32_t f32b{4.0};

  auto r1 = f32 + f16; // OK : f16は`float`に変換され、結果の型は`float`となる
  auto r2 = f32 + b16; // OK : b16は`float`に変換され、結果の型は`float`となる
  //auto r3 = f16 + b16; // エラー!どちらの型も算術変換でもう一方の型に変換できない
  auto r4 = f32 + f32b;  // OK : f32はより大きいサブ順位をもつ`std::float32_t`に変換される
  static_assert(std::is_same_v<decltype(r4), std::float32_t>);
}

オーバーロード解決

  • 浮動小数点数型の変換をともなうオーバーロード解決は、値を維持する変換が優先され、同じ変換順位をもつ他の浮動小数点数型への変換が優先して行われる
    • 値を維持する変換が曖昧な場合、プログラムは不適格となる
      • 「ひとつ上の順位をもつ浮動小数点数型に変換する」のような規則ではないため、ひとつ以上上の順位をもつ浮動小数点数型の候補が複数あると曖昧になる

#include <stdfloat>

void f(std::float32_t) {}
void f(std::float64_t) {}
void f(long long) {}

int main() {
  float x;
  std::float16_t y;
  f(x); // f(std::float32_t)が呼び出される。
        // floatとstd::float32_tが同じ変換順位をもつ
  //f(y); // エラー!曖昧。変換順位が等しいオーバーロードが見つからない
}

備考

  • この型は、C23で定義されるオプションキーワード_Float16の別名として定義されることになるだろう
    • この型は、_Float16と相互運用できるよう実装されることが望ましい
  • この型が定義される環境はISO/IEC/IEEE 60559 (IEEE 754) 準拠であるため、同じ変換順位をもつ浮動小数点数型 (floatstd::float32_tのような) は同じ内部表現をもつ

#include <iostream>
#include <stdfloat>
#include <cmath>

int main() {
  std::float16_t a = 1.0f16;
  auto b = 2.0f32; // bの型はstd::float32_t

  // aはより大きい精度の型float32_tに変換される
  auto c = a + b; // cの型はstd::float32_t

  // 精度を落とす縮小変換は明示的型変換で行う。
  // 拡張浮動小数点数型は数学関数にも渡すことができ、
  // 標準浮動小数点数型への暗黙変換もできる
  double d = std::log(static_cast<std::float16_t>(c));

  // 同じ精度の浮動小数点数型との間で、精度を落とさず変換でき、
  // coutでも拡張浮動小数点数のまま出力できる
  std::cout << static_cast<std::float64_t>(d) << std::endl;
}

出力

1.09863

バージョン

言語

  • C++23

処理系

関連項目

参照