In the beginning there was the unsigned integer, and it was good. If you go deep into machine code you will still find that any device is nothing more than unsigned integers. All other values Int
, Float
, Bool
, Character
, and String
are forms or collections of unsigned integers. To a modern high-level language like Swift, we don’t think about this much. However there are times unsigned integers become important.
The Difference Between an Unsigned and Signed Integer.
Before we begin, Here’s a little background for those not familiar with some core concepts in computer science. We normally measure data storage units in bytes. Each byte is represented by a binary number comprised of bits. Depending on the processor in a computer the number of bits is different. For example, an Apple ][+ was an 8-bit byte. iPhones up to the iPhone 5 are 32 bit bytes, from the iPhone 5 on, iPhones were 64 bit bytes.
Since a bit can be either on of off, we have two states. Computer scientists realized that a series of bits could add up by power of two. A four bit number for example could represent the sixteen numbers 0 – 15, An eight bit number could represent the numbers 0-255 , Thirty two bit numbers could have values of 0 to 4,294,967,295. Sixty four bit bytes can have values of 0 to some number so big we really don’t have to think about it (1.844 x 10^19 approximately).
Each bit becomes a power of two – it’s a digit of a binary number. For example, the four bit number 1010 becomes
1 * 8 = 8 0 * 4 = 0 1 * 2 = 2 0 * 1 = 0
Adding the bits together, 8 + 2 = 10. The four-bit binary number 1111 is 15 and the four-bit binary number 0000 is 0. To prevent the binary number getting confused with decimal numbers, the prefix 0b is often used in code and documentation to describe a binary number. 0b1011 is 11 decimal for example.
But these numbers are only positive integers. Subtract 1 from zero, and the program crashes with an exception. For many calculations, we need negative numbers. Computer science came up with a brilliant solution which we call signed integers. Take the first bit of a byte and make it a positive or negative sign. If zero, the value is positive. If one the value is negative. This cuts the number of positive values in half, but adds negative values. Using a four bit example, 0b0101 as a signed integer is 5. However, for reasons I won’t get into here, they did one thing that wasn’t intuitive: negative numbers count down from their maximum value. For example 0b1101 as a signed integer is 0b111 – 0b101 which is -2 not -5 as you’d think intuitively.
Introducing the Flavors of UInt
Since we use number with positive and negative integers more often than positive integers only, the type Int
is the signed integers. If we want a value without a sign, then we use the type UInt
. UInt
creates a integer of the same bit size as the device’s processor can handle. There is only one reason you need a UInt
: when you need some really big integers. But since the maximum integer changes depending on device this is not a great idea.
Where unsigned integers find their use is with a specified number of bits. Swift provides the types UInt8
,UInt16
, UInt32
, UInt64
, which are for 8, 16, 32, and 64 bit numbers. Why do we need these? Because most uses of unsigned integers will specify the number of bits used.
Unsigned integers are pure 1’s and 0’s, and we can use them in several ways. We can make each bit represent the state of something else. This is common in device control. You may use several bits of a byte to turn on or off different parts of the device. Another use is to limit values between two numbers. Many devices, no matter how sophisticated they are, still use traditional eight, sixteen, or thirty two bit values. The standard for RGB color on the web and many non-Apple API’s is a collection of three eight bit (0-255) numbers, one each for red,green, and blue.
Basic Math With Unsigned Integers
Math functions work the same as Int
, with one exception: you have boundaries to watch for. All of these will work with no problems:
let c:UInt8 = 0b00000101 let d:UInt8 = 128 //0b10000000 let e:UInt8 = 255 //0b11111111 let f:UInt8 = 10 //0b00001010 print("Math operators") print(d + 1 ) // 129 print(d - 1 ) // 127 print(f * 2) // 20 print(d / 3) // 42
However, this will return an exception
print(f - 11)
as will this:
print(e + 1)
Both exceed the boundaries of 0-255.
Overflow Math
You might want to cross those boundaries. In those cases we want the value to wrap around to the the other end. For an UInt8
for example, we want 255+1 to be 0 and 0 – 1 to be 255. We use the overflow math operators &+
and &-
print(f &- 11) print(e &+ 1)
Bit Shift Operators
Math operators in unsigned integers are of secondary importance to bitwise operators. We use unsigned integers most often to control individual bits. There are two types of operations: logical operations and shifts.
Shifting moves one bit from its current position to another position We use the operators <<
and >>
to shift. We specify after the operator how many bits we want to move. To move one bit to the right we would use this:
print(f <<t; 1) //0b00000101 = 5
one bit to the left, we would use this:
print(f >> 1) //0b00010100 = 20
A bit shift replaces the empty space of the shift with a zero. Thus 255 (0b11111111) shifted twice places two 0’s at the beginning or end of the value, and dumps two 1’s.
print(e >> 2) //0b00111111 = 63
Signed integers make a mess out of bit shifts because of the negative number representation. For that reason, avoid them and use unsigned integers if you need to make a bit shift.
Logical Operators
We can also perform logical operations on bits. We can use AND(&
) , OR(|
), and NOT(~
) operators for unsigned integers.
print("logical operators") print(~f) //NOT(0b00001010) = 0b11110101 = 245 print(d & e) //0b10000000 AND 0b11111111 = 0b10000000 = 128 print(d | e) //0b10000000 OR 0b11111111 = 0b11111111 = 255
The expression d > e
did not change anything. As bitwise operations work on a bit-to bit comparison, only the leftmost bit was 1 since both were 1. We can use this to make a bit mask, a way filtering bit data to get what we want.
For example, let’s suppose we have a Bluetooth connection to an Arduino controller. The controller transmits back to our application data about ports 0 through 7, which are connected to a set of digital sensors. We want to know when a sensor is on, represented as a 1 for its bit. Since any bit that is 1 creates a non-zero value, check if the value transmitted is non-zero. We could write a function like this:
func isSensorOn(sensors:UInt8) -> Bool{ return sensors != 0 }
However there’s a problem. Pins 0 and 1 are receive and transmit for serial data. They will likely be chattering nonstop with data, messing up our function. We need to filter out pins 0 and 1. We can make a bit mask that says “only give me data for pins 2 through 7” like this:
func isSensorOn(sensors:UInt8) -> Bool{ let bitMask:UInt8 = 0b11111100 return (sensors & bitMask) != 0 }
What if I wanted to get a specific sensor’s reading as a Bool? I could make a bit mask using a shift
func isSensorOnPin(pin:UInt8, sensors:UInt8) -> Bool{ let bitMask:UInt8 = 1 << pin //shift to the correct pin return (sensors & bitMask) != 0 }
It is bit masks which will be the biggest use of unsigned integers you will have. When you are working directly with bytes, they provide a way to examine or change individual bits. Apple’s API’s often don’t require them and does a good job or hiding them in constants, classes and methods. For example, Apple uses a CGFLoat
of 0 to 1.0 instead of 0 to 255 for color values using UIColor(red:green:blue:alpha)
. You will find exceptions, and indeed a few places where bit masks are required. Raw image data, device communications and the like will often need bit masks to work properly. While the beginning developer might not need unsigned integers and bit masks, more advanced developers working with image data directly and communicating with outside devices will find them very useful.
The Whole Code
I’ve posted this code on the IBM Swift Sandbox if you want to play with it. You can find it here. I suggest cuttng and pasting the code there and not here as WordPress has a terrible habit of mangling the code.
// TODO: Write some awesome Swift code, or import libraries like "Foundation", // "Dispatch", or "Glibc" // find this code at the IBM Sandbox at http://swiftlang.ng.bluemix.net/#/repl/15f819c02e0b09f3d01ef07292bee43d06ee0774ff0261655f3b01f159cfac83 // representing numbers in different bases -- not covered in lesson let i:Int = 320 //decimal print(i) let b:Int = 0b1111 //binary print(b) let h:Int = 0xff //hex print(h) let o:Int = 0o77 //octal print(o) //declaring an Unsigned Integer. You must declare the type for this. let a:UInt = 26 print(a) //using operators let c:UInt8 = 0b00000101 let d:UInt8 = 128 //0b10000000 let e:UInt8 = 255 //0b11111111 let f:UInt8 = 10 //0b00001010 print("Math operators") print(d + 1 ) // 129 print(d - 1 ) // 127 print(f * 2) // 20 print(d / 3) // 42 print("Overflow Math operators") print(f &- e) // 11 print(e &+ 2) // 1 print("Bit shift operators") print(f >> 1) //0b00000101 = 5 print(f << 1) //0b00010100 = 20 print(e <> 2) //0b00111111 = 63 print("Logical operators") print(~f) //NOT(0b00001010) = 0b11110101 = 245 print(d & e) //0b10000000 AND 0b11111111 = 0b10000000 = 128 print(d | e) //0b10000000 OR 0b11111111 = 0b11111111 = 256 print("Using bitmasks to get a bit") func isSensorOn(sensors:UInt8) -> Bool{ let bitMask:UInt8 = 0b11111100 return (sensors & bitMask) != 0 } print(isSensorOn(10)) //true print(isSensorOn(11)) //true print(isSensorOn(3)) //false print(isSensorOn(0)) //false func isSensorOnPin(pin:UInt8, sensors:UInt8)->Bool{ let bitMask:UInt8 = 1 << pin //shift to the correct pin return (sensors & bitMask) != 0 } print("Check individual pins") print(isSensorOnPin(3, sensors:10)) //true print(isSensorOnPin(3, sensors:11)) //true print(isSensorOnPin(4, sensors:11)) //false print(isSensorOnPin(5,sensors:255)) //true print(isSensorOnPin(4,sensors:0)) //false
Leave a Reply