Swift Digest
SE-0277 | Swift Evolution

Float16

Proposal
SE-0277
Authors
Stephen Canon
Review Manager
Ben Cohen
Status
Implemented (Swift 5.3)

01 何が問題だったのか

Swift にはこれまで、32 ビットの FloatFloat32)、64 ビットの DoubleFloat64)、そして一部のプラットフォーム向けの Float80 はありましたが、IEEE 754 の binary16 フォーマットに対応する 16 ビット浮動小数点数型(いわゆる half-precisionhalffloat16)が用意されていませんでした。

一方でこの 10 年ほどで、Float より小さな浮動小数点数型の利用は大きく広がっています。なかでも Float16 は、モバイル GPU での計算、HDR 画像のピクセルフォーマット、機械学習の重みを圧縮して保持する用途など、さまざまな場面で使われています。

Swift に Float16 がないと、次のような困りごとが生じます。

  • シェーダ言語で書かれた GPU プログラムに渡すために、CPU 側で Float16 を含むデータ構造を組み立てたいケースでは、unsafe な仕組みに頼ってバイト列を組み立てるしかありません。
  • _Float16 を使う C の API は、対応する Swift の型がないためそもそも import できず、Swift から利用できません。

このように、実務上の需要は明確にあるにもかかわらず、標準ライブラリに型が存在しないことで相互運用が阻まれているのが問題でした。

02 どのように解決されるのか

標準ライブラリに Float16 型を追加します。IEEE 754 の binary16 に対応する 16 ビット浮動小数点数型で、C の _Float16 とコンパイラによってブリッジされます。

型定義は次のとおりで、BinaryFloatingPointSIMDScalarCustomStringConvertible に適合します。

@frozen
struct Float16: BinaryFloatingPoint, SIMDScalar, CustomStringConvertible { }

API 面は、これらのプロトコルへの適合からそのまま導かれるものがすべてで、それ以外に追加されるものはありません。言い換えると、FloatDoubleFloat80 がこれらのプロトコルへの適合として提供しているのと同じ操作が、そのまま Float16 でも使えます。

使い方

他の浮動小数点数型と同じ感覚でリテラルや算術演算、型変換を扱えます。

let x: Float16 = 1.5
let y: Float16 = 2.25
let z = x * y + 1        // Float16

let f = Float(z)         // Float へ変換
let back = Float16(f)    // Float から戻す

SIMD との組み合わせも可能で、たとえば 4 要素ベクトルを次のように作れます。

let v: SIMD4<Float16> = [1, 2, 3, 4]
let w = v * 2

GPU に渡すためのデータ構造を Swift 側で組み立てる、_Float16 を使う C の API を呼び出す、といった用途がそのまま書けるようになります。

ネイティブ対応がないプラットフォームでの挙動

ハードウェアに Float16 の演算サポートがないプラットフォームでは、実行時に Float へ変換して Float のハードウェア演算を行い、結果を Float16 に戻すことで演算を実現します。この方法は、fused multiply-add を除くすべての演算について正しく丸められます。fused multiply-add については、Double を経由するなどソフトウェアで模倣した実装が使われます。

パラメータ渡しの規約としては、スカラー値は浮動小数点数レジスタで、ベクトル値は SIMD レジスタで渡したり返したりするように調整されます。

Float16 という名前について

Half ではなく Float16 という名前が選ばれているのは、Swift がすでに持っている Float32Float64Float80 という FloatN パターンと揃えるためです(FloatDouble はそれぞれ Float32Float64 の別名という位置づけです)。将来的に Float128 を追加する余地も見据えています。また、ブリッジ先である C 側の型名も _Float16 であり、命名の一貫性が保たれます。

スコープ外の話題

次のものは今回の提案の対象外です。

  • ARM の「alternative half precision」への対応。現行の ARMv8.2-FP16 では IEEE 754 エンコーディングのみが使われており、旧来の alternative half precision はすでにサポートされていないためです。
  • bfloat16 への対応。将来的にサポートすべきではあるものの、ハードウェア仕様が固まりきっていないため、別の Proposal として切り分けられています。
  • 数学関数ライブラリの提供。このProposal が承認された後、Swift Numerics 側で Real への適合を追加し、Float の実装を利用する形で数学関数を提供する方針が示されています。