128-bit Integer Types
01 何が問題だったのか
Swiftの標準ライブラリには、Int8 / Int16 / Int32 / Int64 と、それぞれに対応する符号なし版の固定幅整数型が用意されていますが、幅の最大は64ビットまででした。一方で、128ビット整数は汎用的なコードで日常的に使われる最大幅の固定幅整数として、暗号処理、ハッシュ値、UUID、大きなカウンタ、通貨や時間の高精度計算など、64ビットでは足りない場面で広く求められています。
Swift の標準ライブラリ自身、Duration の内部表現のように既に128ビット整数を利用している箇所があります。それにもかかわらず標準の Int128 / UInt128 が公開されていないため、ライブラリ作者もアプリ開発者も、必要になったときに自前で実装したり、Cの __int128_t / __uint128_t を経由したりと、場当たり的な対応を強いられていました。また、Cの __int128_t / __uint128_t で表現された値をSwiftへブリッジする際にも、受け皿となる標準型が無いという問題がありました。
他の幅の固定幅整数(Int256 など)も考えられますが、現時点で汎用ソフトウェアに対する有用性は128ビットが桁違いに高く、まずは128ビットを標準ライブラリに入れる価値が十分にあると判断されました。
02 どのように解決されるのか
標準ライブラリに、128ビット幅の符号付き整数型 Int128 と符号なし整数型 UInt128 が追加されます。いずれも構造体として定義され、固定幅整数として期待される一連のプロトコルに適合します。
let a: UInt128 = 0xffff_ffff_ffff_ffff_ffff_ffff_ffff_ffff
let b: UInt128 = 1
let c = a &+ b // 0(ラップアラウンド)
let signed: Int128 = -170_141_183_460_469_231_731_687_303_715_884_105_728
print(Int128.max) // 170141183460469231731687303715884105727
print(Int128.min) // -170141183460469231731687303715884105728
他の整数型と同様に、整数リテラルや算術演算子、ビット演算子、比較、文字列変換などが一通り使えます。
適合するプロトコル
Int128 / UInt128 のAPI自体は、他の固定幅整数型と同じくプロトコル適合から導かれるもので、特別なメソッドは持ちません。適合する主なプロトコルは次のとおりです。
Hashable/Equatable/ComparableCodableSendableLosslessStringConvertibleExpressibleByIntegerLiteralAdditiveArithmetic/Numeric/SignedNumeric(Int128のみ)BinaryInteger/FixedWidthIntegerUnsignedInteger(UInt128)/SignedInteger(Int128)
また、_hasAtomicBitWidth(_128) が有効なターゲット(x86_64、arm64、arm64_32など)では AtomicRepresentable にも適合し、アトミック操作の対象としても使えます。
メモリレイアウトとCとのブリッジ
Int128 / UInt128 のアラインメントは、64ビットターゲットでは16バイト、32ビットターゲットでは Int64 / UInt64 と同じになります。エンディアンは他の整数型と一致します。
Clangインポータも更新され、Cの __int128_t は Int128 に、__uint128_t は UInt128 にブリッジされます。一方、C23で導入された _BitInt(128) については、アラインメントをはじめとするABIの仕様が各プラットフォームでまだ揺れている段階のため、現時点ではブリッジ対象から外されています。
Codable の取り扱い
Int128 / UInt128 の符号化・復号は、エンコーダ/デコーダ側がそれぞれ最適な表現(ネイティブな128ビット値、2つの64ビット値の組、文字列など)を自由に選べるように設計されています。そのために、KeyedEncodingContainerProtocol / KeyedDecodingContainerProtocol / UnkeyedEncodingContainer / UnkeyedDecodingContainer / SingleValueEncodingContainer / SingleValueDecodingContainer の各プロトコルに、Int128 / UInt128 用の encode / decode 要件が追加されます。
これらの要件にはデフォルト実装が用意されていますが、その中身は無条件にエラーを投げるものです。具体的には、エンコード側は EncodingError.invalidValue、デコード側は DecodingError.typeMismatch をスローします。
struct Big: Codable {
var value: UInt128
}
// エンコーダ/デコーダが Int128 / UInt128 に対応していなければ、
// エンコード時は EncodingError.invalidValue、
// デコード時は DecodingError.typeMismatch が投げられる。
この方針により、対応済みのエンコーダ/デコーダではそれぞれの最適な表現で符号化・復号でき、未対応のものは「既定のフォールバック表現」との互換性を気にせず、後から自由に独自の表現を採用できます。利用側から見ると、扱う Codable パイプラインが Int128 / UInt128 に対応しているかどうかを意識する必要があります。
NSNumber へのブリッジは無し
Int128 / UInt128 は NSNumber へはブリッジされません。型消去された数値の扱いについては将来的に改めて整理する予定で、現時点の仕組みに新しい型を追加するのは避けられています。
採用にあたっての注意
Int128 / UInt128 を使うには、それらをサポートするランタイムを持つターゲットが必要です。古いOSをデプロイターゲットにしている場合は、可用性チェックが必要になります。
今後の展望
_BitInt(128) のABIが各プラットフォームで確定した段階で、Clangインポータ側での _BitInt(128) のブリッジ対応が追加される可能性があります。また、NSNumber を含む型消去された数値の扱いも、将来的により広い文脈で見直される可能性があります。いずれも現時点では見通しの段階であり、本proposalの範囲には含まれません。