来源  Official documents

 

  1. 位运算符
  2. 溢出运算符
  3. 运算符函数
  4. 自定义运算符

 

位运算符

位操作符通常在诸如图像处理和创建设备驱动等底层开发中使用,使用它可以单独操作数据结构中原始数据的比特位。在使用一个自定义的协议进行通信的时候,运用位运算符来对原始数据进行编码和解码也是非常有效的。

位取反运算符

按位取反运算符~对一个操作数的每一位都取反。

let initialBits: UInt8 = 0b00001111 
let invertedBits = ~initialBits  // 等于 0b11110000 

 

位与运算符

位与运算符对两个数进行操作,然后返回一个新的数,两个输入数的同一位都为1时才为1。

 tp

let firstSixBits: UInt8 = 0b11111100 
let lastSixBits: UInt8  = 0b00111111 
let middleFourBits = firstSixBits & lastSixBits  // 等于 00111100 

 

位或运算

位或运算符|比较两个数,两个输入数的同一位只要有一个为1就为1,否则为0

tp

 

let firstBits: UInt8 = 0b00010100 
let otherBits: UInt8 = 0b00000101 
let outputBits = firstBits ^ otherBits  // 等于 00010001 
 左移/右移运算符

左移和右移的效果相当把一个整数乘于或除于一个因子为2的整数。向左移动n个整型的比特位相当于把这个数乘于2的n次方,向右移一位就是除于2的n次方。

无符整型的移位操作

对无符整型的移位的效果如下:

已经存在的比特位向左或向右移动指定的位数。被移出整型存储边界的的位数直接抛弃,移动留下的空白位用零0来填充。这种方法称为逻辑移位。

 

以下这张把展示了 11111111 << 1(11111111向左移1位),和 11111111 >> 1(11111111向右移1位)。蓝色的是被移位的,灰色是被抛弃的,橙色的0是被填充进来的。

tp

let shiftBits: UInt8 = 4   // 即二进制的00000100 
shiftBits << 1             // 00001000 
shiftBits << 2             // 00010000 
shiftBits << 5             // 10000000 
shiftBits << 6             // 00000000 
shiftBits >> 2             // 00000001 

注:十六进制中每两个字符是8比特位

 

有符号整型的移位操作

有符整型的移位操作相对复杂得多,因为正负号也是用二进制位表示的。(这里举的例子虽然都是8位的,但它的原理是通用的。)

 

有符整型通过第1个比特位(称为符号位)来表达这个整数是正数还是负数。0代表正数,1代表负数。

符号位为0,代表正数,另外7比特位二进制表示的实际值就刚好是4。

负数呢,跟正数不同。负数存储的是2的n次方减去它的绝对值,n为数值位的位数。一个8比特的数有7个数值位,所以是2的7次方,即128。

我们来看-4存储的二进制结构。

现在符号位为1,代表负数,7个数值位要表达的二进制值是124,即128 - 4。

对有符号整型按位右移时,使用符号位(正数为0,负数为1)填充空白位。

 

溢出运算符

在Swift中所有允许溢出的运算符都是以&开始的,Swfit为整型计算提供了5个&符号开头的溢出运算符:

  • 溢出加法 &+
  • 溢出减法 &-
  • 溢出乘法 &*
  • 溢出除法 &/
  • 溢出求余 &%
值的上溢出

下面例子使用了溢出加法&+来解剖的无符整数的上溢出

var willOverflow = UInt8.max 
// willOverflow 等于UInt8的最大整数 255 
willOverflow = willOverflow &+ 1 
// 这时候 willOverflow 等于 0 

过程如下图所示:

值的下溢出
var willUnderflow = UInt8.min 
// willUnderflow 等于UInt8的最小值0 
willUnderflow = willUnderflow &- 1 
// 此时 willUnderflow 等于 255 

 

有符号溢出

有符整型也有类似的下溢出,有符整型所有的减法也都是对包括在符号位在内的二进制数进行二进制减法的,这在 "按位左移/右移运算符" 一节提到过。最小的有符整数是-128,即二进制的10000000。用溢出减法减去去1后,变成了01111111,即UInt8所能承载的最大整数127。

Swift代码:

var signedUnderflow = Int8.min 
// signedUnderflow 等于最小的有符整数 -128 
signedUnderflow = signedUnderflow &- 1 
// 如今 signedUnderflow 等于 127 

 

除零溢出

一个数除于0 i / 0,或者对0求余数 i % 0,就会产生一个错误。

let x = 1 
let y = x / 0 

使用它们对应的可溢出的版本的运算符&/和&%进行除0操作时就会得到0值。

let x = 1 
let y = x &/ 0 
// y 等于 0 

 

运算符函数

运算符函数分为三种:

  1. infix 中置运算符
  2. prefix  前置运算符
  3. postfix  后置运算符

在关键字func之前写上这些属性 可以定义一个运算符函数

 

struct Vector2D {
    var x = 0.0, y = 0.0
}
func + (left: Vector2D, right: Vector2D) -> Vector2D {
    return Vector2D(x: left.x + right.x, y: left.y + right.y)
}
let vector = Vector2D(x: 3.0, y: 1.0)
let anotherVector = Vector2D(x: 2.0, y: 4.0)
let combinedVector = vector + anotherVector
// combinedVector is a Vector2D instance with values of (5.0, 5.0)

 

2、前置(prefix)、后置(postfix)运算符
prefix func - (vector: Vector2D) -> Vector2D {
    return Vector2D(x: -vector.x, y: -vector.y)
}

let positive = Vector2D(x: 3.0, y: 4.0)
let negative = -positive
// negative is a Vector2D instance with values of (-3.0, -4.0)
let alsoPositive = -negative
// alsoPositive is a Vector2D instance with values of (3.0, 4.0)

 

3、组合赋值运算符
把运算符的左参数设置成inout,因为这个参数会在运算符函数内直接修改它的值。
func += (inout left: Vector2D, right: Vector2D) {
    left = left + right
}
var original = Vector2D(x: 1.0, y: 2.0)
let vectorToAdd = Vector2D(x: 3.0, y: 4.0)
original += vectorToAdd
// original now has values of (4.0, 6.0)

prefix func ++ (inout vector: Vector2D) -> Vector2D {
    vector += Vector2D(x: 1.0, y: 1.0)
    return vector
}
var toIncrement = Vector2D(x: 3.0, y: 4.0)
let afterIncrement = ++toIncrement
// toIncrement now has values of (4.0, 5.0)
// afterIncrement also has values of (4.0, 5.0)

 

注意:默认的赋值符是不可重载的。只有组合赋值符可以重载。三目条件运算符 a?b:c 也是不可重载。

 

比较运算符

定义比较运算符函数和定义其它中置运算符相同:

 

func == (left: Vector2D, right: Vector2D) -> Bool {
    return (left.x == right.x) && (left.y == right.y)
}
func != (left: Vector2D, right: Vector2D) -> Bool {
    return !(left == right)
}

 

 

 

使用这两个运算符来判断两个Vector2D对象是否相等。

 

let twoThree = Vector2D(x: 2.0, y: 3.0)
let anotherTwoThree = Vector2D(x: 2.0, y: 3.0)
if twoThree == anotherTwoThree {
    print("这两个 vectors 是相等的.")
}
// prints "这两个 vectors 是相等的."

 

 

 

let twoThree = Vector2D(x: 2.0, y: 3.0) 
let anotherTwoThree = Vector2D(x: 2.0, y: 3.0) 
if twoThree == anotherTwoThree { 
    println("这两个向量是相等的.") 
} 
// prints "这两个向量是相等的." 

 

自定义运算符

标准的运算符不够玩,那你可以声明一些个性的运算符,但个性的运算符只能使用这些字符 / = - + * % < >!& | ^。~。

// 1、在全局域使用operator关键字声明一个新的运算符,可以声明为infix,prefix或postfix。
operator prefix +++ {} 

// 2、实现这个运算符的功能
prefix func +++ (inout vector: Vector2D) -> Vector2D { 
    vector += vector //  加自己并返回
    return vector 
}

var toBeDoubled = Vector2D(x: 1.0, y: 4.0) 
let afterDoubling = +++toBeDoubled 
// toBeDoubled 现在是 (2.0, 8.0) 
// afterDoubling 现在也是 (2.0, 8.0) 
自定义运算符的优先级和结合性

结合性用关键字:(associativity),默认值为none.可以取值为left、right和none

优先级用关键字:(precedence),默认为100

以下例子定义了一个新的infix +- 操作符,是左结合的left,优先级为140:

 

infix operator +- { associativity left precedence 140 }
func +- (left: Vector2D, right: Vector2D) -> Vector2D {
    return Vector2D(x: left.x + right.x, y: left.y - right.y)
}
let firstVector = Vector2D(x: 1.0, y: 2.0)
let secondVector = Vector2D(x: 3.0, y: 4.0)
let plusMinusVector = firstVector +- secondVector
// plusMinusVector is a Vector2D instance with values of (4.0, -2.0)

这个运算符把两个向量的x相加,把向量的y相减。因为他实际是属于加减运算,所以让它保持了和加法一样的结合性和优先级(left和140)。

 

2015-03-26

11:56:43

posted on 2015-04-01 13:34  道无涯  阅读(217)  评论(0编辑  收藏  举报