在 Swift 中,BinaryInteger 是一个协议,代表了所有二进制整数类型的通用特性和功能。它是整数类型的一个高级抽象,可以被 Swift 的标准整数类型(如 Int, UInt, Int8, UInt8 等)遵循,同时允许自定义的整数类型实现该协议。
1、协议定义:
BinaryInteger 是继承自 Numeric 和 Hashable 的协议,因此它具有数值操作和哈希功能。
2、应用范围:
Swift 中所有的整数类型(有符号和无符号)都遵循 BinaryInteger。
可以扩展 BinaryInteger 来为所有整数类型提供通用功能。
3、功能特点:
提供了整数类型的基本运算符支持(+, -, *, /, % 等)。
支持位操作(<<, >>, &, |, ~ 等)。
提供与不同类型的转换方法。
支持字节表示和整数的多进制操作。
常用方法和属性
BinaryInteger 协议提供了一些常用的功能和属性:
类型转换
1、init<T>(exactly source: T):从另一个数字类型进行精确初始化,如果不能表示则返回 nil。
检查目标类型是否可以精确表示 source。
如果值超出目标类型的范围(比如试图将一个 Int64 转换为 Int8 而数值超出了 Int8 的表示范围),则返回 nil。
let value = Int8(exactly: 300) // 返回 nil,因为 300 超出 Int8 范围
let value2 = Int8(exactly: 127) // 返回 127,因为在范围内
2、init<T>(_ source: T):从其他数字类型初始化,可能会截断。
将源类型的值转换为目标类型。如果超出范围,会保留低位部分(截断高位)。
let truncated = Int8(300) // 300 的二进制低位部分是 44,因此返回 44
3、magnitude:返回无符号值的绝对值。
如果值是负数,返回 -self 的无符号值;如果是正数,直接返回。
let x: Int = -5
print(x.magnitude) // 输出 5
位操作
1、leadingZeroBitCount:返回前导零的位数。
检查整数的二进制表示,从高位到低位统计连续零的数量,直到第一个非零位。
通常依赖硬件指令或库函数来计算。
let x: UInt8 = 0b00001111
print(x.leadingZeroBitCount) // 输出 4
2、trailingZeroBitCount:返回尾随零的位数。
检查整数的二进制表示,从低位到高位统计连续零的数量,直到第一个非零位。
let x: UInt8 = 0b00010000
print(x.trailingZeroBitCount) // 输出 4
3、bitWidth:整数类型的位宽。
对于固定宽度的类型,如 Int8、Int16 等,直接返回其位宽(例如 Int8.bitWidth = 8)。
对于其他自定义整数类型,可能需要动态计算。
let x: Int8 = 127
print(x.bitWidth) // 输出 8
4、nonzeroBitCount:非零位的数量。
遍历整数的二进制表示,统计所有 1 的个数。
常见实现是基于硬件加速的“汉明重量”算法。
let x: UInt8 = 0b11010010
print(x.nonzeroBitCount) // 输出 4
数值属性
1、isMultiple(of:):判断是否是某个数的倍数。
检查 self % other == 0。
let x = 12
print(x.isMultiple(of: 3)) // 输出 true
print(x.isMultiple(of: 5)) // 输出 false
2、signum():返回整数的符号(-1、0 或 1)。
实现逻辑:
如果 self > 0,返回 1;
如果 self == 0,返回 0;
如果 self < 0,返回 -1。
let x = -42
print(x.signum()) // 输出 -1
let y = 0
print(y.signum()) // 输出 0
let z = 7
print(z.signum()) // 输出 1
运算符支持
1、标准算术运算符:+, -, *, /, %。
2、位运算符:&, |, ^, ~, <<, >>。
<< 和 >>:左移和右移,通常是基于硬件指令。
let x: UInt8 = 0b1100
print(x << 2) // 输出 0b110000
3、比较运算符:<, <=, >, >=.
示例代码
func isEven<T: BinaryInteger>(_ number: T) -> Bool {
return number.isMultiple(of: 2)
}
let intNumber: Int = 4
let uintNumber: UInt = 7
let int8Number: Int8 = -6
print(isEven(intNumber)) // true
print(isEven(uintNumber)) // false
print(isEven(int8Number)) // true
应用示例
位操作示例
let value: Int = 12 // 二进制: 1100
print(value.leadingZeroBitCount) // 比如,在 64 位系统中输出 60
print(value.trailingZeroBitCount) // 输出 2,因为最低两位是 0
print(value.nonzeroBitCount) // 输出 2,因为有两个 1
自定义类型遵循 BinaryInteger
可以创建一个自定义类型,并让它遵循 BinaryInteger,从而使其具备整数类型的行为:
struct MyInt: BinaryInteger {
private var value: Int
// 必须实现的属性和方法
var magnitude: UInt { return UInt(abs(value)) }
init<T>(_ source: T) where T: BinaryInteger {
self.value = Int(source)
}
init<T>(exactly source: T) where T: BinaryInteger? {
guard let value = source.flatMap({ Int(exactly: $0) }) else {
return nil
}
self.value = value
}
// 其余实现省略...
}
值得注意的是,在 Swift 中,实现复杂的协议(例如 BinaryInteger)需要实现许多协议要求。BinaryInteger 继承了多个协议,包括 AdditiveArithmetic、Numeric 和 ExpressibleByIntegerLiteral 等,每个协议都有具体的要求。
因此,不推荐自定义类型遵循BinaryInteger协议。
总结
BinaryInteger 是 Swift 提供的一个协议,用于描述和抽象整数类型的通用行为。它使得所有整数类型共享一套功能,并且可以通过泛型和扩展来简化跨类型的操作。
附录
自定义类型遵循BinaryInteger代码
以下是自定义类型遵循BinaryInteger协议的样例,但是仍然存在报错:
Type 'MyInt' does not conform to protocol 'BinaryInteger'
所以,仅作为了解参考。
struct MyInt: BinaryInteger {
private var value: Int
var magnitude: UInt { UInt(abs(value)) }
var words: [UInt] { value.words.map { UInt($0) } }
var bitWidth: Int { value.bitWidth }
var nonzeroBitCount: Int { value.nonzeroBitCount }
var leadingZeroBitCount: Int { value.leadingZeroBitCount }
var trailingZeroBitCount: Int { value.trailingZeroBitCount }
func hash(into hasher: inout Hasher) {
hasher.combine(value)
}
init<T>(_ source: T) where T: BinaryInteger {
self.value = Int(source)
}
init?<T>(exactly source: T) where T: BinaryInteger {
guard let value = Int(exactly: source) else {
return nil
}
self.value = value
}
init(integerLiteral value: Int) {
self.value = value
}
init<T>(truncatingIfNeeded source: T) where T: BinaryInteger {
self.value = Int(truncatingIfNeeded: source)
}
init<T>(clamping source: T) where T: BinaryInteger {
self.value = Int(clamping: source)
}
init(bitPattern value: UInt) {
self.value = Int(bitPattern: value)
}
static func + (lhs: MyInt, rhs: MyInt) -> MyInt {
MyInt(lhs.value + rhs.value)
}
static func - (lhs: MyInt, rhs: MyInt) -> MyInt {
MyInt(lhs.value - rhs.value)
}
static func * (lhs: MyInt, rhs: MyInt) -> MyInt {
MyInt(lhs.value * rhs.value)
}
static func / (lhs: MyInt, rhs: MyInt) -> MyInt {
MyInt(lhs.value / rhs.value)
}
static func % (lhs: MyInt, rhs: MyInt) -> MyInt {
MyInt(lhs.value % rhs.value)
}
static var zero: MyInt { MyInt(0) }
static prefix func - (operand: MyInt) -> MyInt {
MyInt(-operand.value)
}
static func == (lhs: MyInt, rhs: MyInt) -> Bool {
lhs.value == rhs.value
}
static func < (lhs: MyInt, rhs: MyInt) -> Bool {
lhs.value < rhs.value
}
static func & (lhs: MyInt, rhs: MyInt) -> MyInt {
MyInt(lhs.value & rhs.value)
}
static func | (lhs: MyInt, rhs: MyInt) -> MyInt {
MyInt(lhs.value | rhs.value)
}
static func ^ (lhs: MyInt, rhs: MyInt) -> MyInt {
MyInt(lhs.value ^ rhs.value)
}
static func << (lhs: MyInt, rhs: MyInt) -> MyInt {
MyInt(lhs.value << rhs.value)
}
static func >> (lhs: MyInt, rhs: MyInt) -> MyInt {
MyInt(lhs.value >> rhs.value)
}
static func <<= (lhs: inout MyInt, rhs: MyInt) {
lhs = MyInt(lhs.value << rhs.value)
}
static func >>= (lhs: inout MyInt, rhs: MyInt) {
lhs = MyInt(lhs.value >> rhs.value)
}
static prefix func ~ (operand: MyInt) -> MyInt {
MyInt(~operand.value)
}
static func += (lhs: inout MyInt, rhs: MyInt) {
lhs = lhs + rhs
}
static func -= (lhs: inout MyInt, rhs: MyInt) {
lhs = lhs - rhs
}
static func *= (lhs: inout MyInt, rhs: MyInt) {
lhs = lhs * rhs
}
static func /= (lhs: inout MyInt, rhs: MyInt) {
lhs = lhs / rhs
}
static func %= (lhs: inout MyInt, rhs: MyInt) {
lhs = lhs % rhs
}
}