溢出运算符
默认情况下,当你往一个整型常量或变量赋于一个它不能承载的大数时,Swift不会让你这么干的,它会报错。这样,在操作过大或过小的数的时候就很安全了。
例如,Int16
整型能承载的整数范围是-32768
到32767
,如果给它赋上超过这个范围的数,就会报错:
var potentialOverflow = Int16.max
// potentialOverflow 等于 32767, 这是 Int16 能承载的最大整数
potentialOverflow += 1
// 噢, 出错了
对过大或过小的数值进行错误处理让你的数值边界条件更灵活。
当然,你有意在溢出时对有效位进行截断,你可采用溢出运算,而非错误处理。Swfit为整型计算提供了5个&
符号开头的溢出运算符。
- 溢出加法
&+
- 溢出减法
&-
- 溢出乘法
&*
- 溢出除法
&/
- 溢出求余
&%
值的上溢出
下面例子使用了溢出加法&+
来解剖的无符整数的上溢出
var willOverflow = UInt8.max
// willOverflow 等于UInt8的最大整数 255
willOverflow = willOverflow &+ 1
// 这时候 willOverflow 等于 0
willOverflow
用Int8
所能承载的最大值255
(二进制11111111
),然后用&+
加1。然后UInt8
就无法表达这个新值的二进制了,也就导致了这个新值上溢出了,大家可以看下图。溢出后,新值在UInt8
的承载范围内的那部分是00000000
,也就是0
。
值的下溢出
数值也有可能因为太小而越界。举个例子:
UInt8
的最小值是0
(二进制为00000000
)。使用&-
进行溢出减1,就会得到二进制的11111111
即十进制的255
。
Swift代码是这样的:
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.位运算符
1.1按位取反
前置运算符 ~ 对一个操作数的每一位都取反,后紧跟操作数,不加空格- let initialBits: UInt8 = 0b00001111
- let invertedBits = ~initialBits // equals 11110000
1.2按位与
对两个数进行操作,返回一个新的数,两个数的同一位都为1时,输出数才为1- let firstSixBits: UInt8 = 0b11111100
- let lastSixBits: UInt8 = 0b00111111
- let middleFourBits = firstSixBits & lastSixBits // equals 00111100
1.3按位或
比较两个数,返回一个新的数,两个数的同一位都不为0时,输出数才为1- let someBits: UInt8 = 0b10110010
- let moreBits: UInt8 = 0b01011110
- let combinedbits = someBits | moreBits // equals 11111110
1.4按位异或
比较两个数返回一个新的数,两个数的同一位不同时,输出数才为1,相同输出0- let firstBits: UInt8 = 0b00010100
- let otherBits: UInt8 = 0b00000101
- let outputBits = firstBits ^ otherBits // equals 00010001
1.5按位左移/右移
把一个数的比特位按定义的规则向左或向右移动指定的位数,相当于把一个整数乘以或除以一个因子为2的整数,左移相当于乘以2,右移相当于除以21.5.1无符号
向左移或向右移动指定的位数,被移出整型存储边界的位数直接抛弃,移动过留下的空白位用零填充,称为逻辑移位
- let shiftBits: UInt8 = 4 // 00000100 in binary
- shiftBits << 1 // 00001000
- shiftBits << 2 // 00010000
- shiftBits << 5 // 10000000
- shiftBits << 6 // 00000000
- shiftBits >> 2 // 00000001
- let pink: UInt32 = 0xCC6699
- let redComponent = (pink & 0xFF0000) >> 16 // redComponent is 0xCC, or 204
- let greenComponent = (pink & 0x00FF00) >> 8 // greenComponent is 0x66, or 102
- let blueComponent = pink & 0x0000FF // blueComponent is 0x99, or 153
1.5.2有符号
有符号整型通过第一个比特位来表达这个数是整数还是负数,0代表正数,1代表负数
有符整型在右移的时候,使用符号位(0/1)填补右移产生的空白,以确保有符号的整型在右移的时候符号不会发生变化
2.溢出运算符
默认Swift不允许往一个整型常量赋予一个它无法承载的大数,这样在操作过大或过小的数时就会很安全- // Int16整型能接受的范围是-32768到32767之间,
- var potentialOverflow = Int16.max
- // potentialOverflow equals 32767, which is the largest value an Int16 can hold
- potentialOverflow += 1
- // this causes an error
如果有意在溢出时对有效位进行截断,可采用溢出运算,Swift提供5个&字符开头的溢出运算符
2.1值的上溢出 (&+)
- var willOverflow = UInt8.max
- // willOverflow equals 255, which is the largest value a UInt8 can hold
- willOverflow = willOverflow &+ 1
- // willOverflow is now equal to 0
2.2值的下溢出 (&-)
- var willUnderflow = UInt8.min
- // willUnderflow equals 0, which is the smallest value a UInt8 can hold
- willUnderflow = willUnderflow &- 1
- // willUnderflow is now equal to 255
2.3除零溢出 (&/)
- let x = 1
- let y = x / 0
- let x = 1
- let y = x &/ 0
- // y is equal to 0
3.优先级和结合性
运算符的优先级是的一些运算符优先与其他运算符,高优先级的运算符会先计算- 2 + 33 * 4 % 5
- // this equals 4
- 2 + ((33 * 4) % 5)
4.运算符函数
让已有的运算符也可以对自定义的类和结构体进行运算,称为运算符重载在func之前写上属性 @infix 即可定义一个全局的 + 函数
- // 参数命名为left 和 right 表示 + 左边和有右边的对象
- struct Vector2D {
- var x = 0.0, y = 0.0
- }
- @infix 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)
4.1前置和后置运算符
实现前置后者后置运算符,在定义该运算符的时候与关键字之前标注 @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)
4.2组合赋值运算符
组合赋值是其他运算符和赋值运算符一起执行的运算,如 += 把加运算和赋值运算符组合成一个操作,实现一个组合赋值符号需要用 @assignment 属性,还要把运算符的左参数设置成inout, 因为这个参数会在运算符函数内修改这个值- @assignment 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)
- // 以下把 @assignment和 @prefix 或 @postfix属性结合起来
- @prefix @assignment 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)
4.3比较运算符
- @infix func == (left: Vector2D, right: Vector2D) -> Bool {
- return (left.x == right.x) && (left.y == right.y)
- }
- @infix func != (left: Vector2D, right: Vector2D) -> Bool {
- return !(left == right)
- }
- let twoThree = Vector2D(x: 2.0, y: 3.0)
- let anotherTwoThree = Vector2D(x: 2.0, y: 3.0)
- if twoThree == anotherTwoThree {
- println("These two vectors are equivalent.")
- }
- // prints "These two vectors are equivalent."
5.自定义运算符
自定义的运算符只能使用以下字符 : / = - + * % < > ! & | ^ 。~新的运算符声明需要在全局使用operator关键字声明,可以声明为前置,中置或后置
- operator prefix +++ {}
定义一个新的前置运算符 +++
- @prefix @assignment func +++ (inout vector: Vector2D) -> Vector2D {
- vector += vector
- return vector
- }
- var toBeDoubled = Vector2D(x: 1.0, y: 4.0)
- let afterDoubling = +++toBeDoubled
- // toBeDoubled now has values of (2.0, 8.0)
- // afterDoubling also has values of (2.0, 8.0)
5.1自定义中置运算符的优先级和结合性
可为自定义的中置运算符指定优先级和结合性,结合性可取值的范围有left,right和none,左结合运算符跟其他优先级相同的左结合写在一起时,会跟左边的操作数结合,同理,右结合运算符会跟右边的操作数结合,非结合运算符不能跟其他相同优先级的运算符写在一起- operator infix +- { 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)