CPSC 355: Tutorial 5

Binary and Other Representations

PhD Student

Fall 2017

Outline

  • The computers we use are binary by design
  • 0 is a low voltage (around 0v), and 1is a high voltage (for TTL logic, this is around 3.3v)
  • All numbers are represented in binary within a computer

Unsigned Integers

Binary Decimal
0000 0
0001 1
0010 2
0011 3
0100 4
0101 5
0110 6
0111 7
1000 8
1001 9
1010 10
1011 11
1100 12
1101 13
1110 14
1111 15

Consider a bit number, the representation we choose is a base 2 representation, that is, if we have some binary number

\displaystyle\sum^3_{i=0} b_i2^i
i=03bi2i\displaystyle\sum^3_{i=0} b_i2^i
b_3b_2b_1b_0
b3b2b1b0b_3b_2b_1b_0

Then the decimal representation is

1010 = 1 \cdot 2^3 + 0 \cdot 2^2 + 1 \cdot 2^1 + 0 \cdot 2^0 = 10
1010=123+022+121+020=101010 = 1 \cdot 2^3 + 0 \cdot 2^2 + 1 \cdot 2^1 + 0 \cdot 2^0 = 10

For example

Unsigned Integers

In general, for an n-bit number, we have the binary number

\displaystyle\sum^{n-1}_{i=0} b_i2^i
i=0n1bi2i\displaystyle\sum^{n-1}_{i=0} b_i2^i
b_{n-1} \cdots b_3b_2b_1b_0
bn1b3b2b1b0b_{n-1} \cdots b_3b_2b_1b_0

equivalent to the decimal number

Octal numbers

Binary Octal Decimal
0000 0 0
0001 1 1
0010 2 2
0011 3 3
0100 4 4
0101 5 5
0110 6 6
0111 7 7
1000 10 8

Octal representation, or a base 8 representation, is similar, but with each digit taking a value between 0-7.

One octal digit is a group of 3 bits.

Hexadecimal numbers

Binary Octal Decimal Hexadecimal
0000 0 0 0
0001 1 1 1
0010 2 2 2
0011 3 3 3
0100 4 4 4
0101 5 5 5
0110 6 6 6
0111 7 7 7
1000 10 8 8
1001 11 9 9
1010 12 10 A
1011 13 11 B
1100 14 12 C
1101 15 13 D
1110 16 14 E
1111 17 15 F

Hexadecimal representation, or a base 16 representation, is similar, but with each digit taking a value between 0-F.

One hexadecimal digit is a group of 4 bits.

Representations in C

#include <stdio.h> // Include functions from the standard I/O library

int main(int argc, char *argv[]) 
{
    // "Literals" are a sort of way of writing values in a different way
    int v3 = 0xFEEDDAD; // Is a hexadecimal literal
    int v4 = 0b10010011; // Is a binary literal (but this is not standard C!)
    int v5 = 022222222; // an octal literal (notice the first 0).

    printf("v3 = %d, 0x%x, 0%o\n", v3, v3, v3);
    printf("v4 = %d, 0x%x, 0%o\n", v4, v4, v4);
    printf("v5 = %d, 0x%x, 0%o\n", v5, v5, v5);
}

Bitwise Operations

There are a couple of very useful operations on binary numbers

  • Binary OR, | short hand
  • Binary AND, & short hand
  • Binary NOT, ~ short hand
  • Binary XOR, ^ short hand
  • Binary Shift Left and Right

These are useful building blocks for arithmetic operations

Bitwise OR

An OR between two bits a, and b, truth table

a\b 0 1
0 0 1
1 1 1
    1001
 OR 0101
--------
    1101

Example

Think of this as "forcing bits to be set" in a register

Bitwise OR

#include <stdio.h>

void main(int argc, char *argv[]) {
    char a = 0b1001;
    char b = 0b0111;

    printf("a=0x%x, b=0x%x\na|b = 0x%x\n", a, b, a|b);
}
fmt: .string "a=0x%x, b=0x%x\na|b = 0x%x\n"
    .balign 4
    .global main

main:
    stp  x29, x30, [sp, -16]! 
    mov  x29, sp     
    ldr  x0, =fmt           
    mov  x1, 0b1001
    mov  x2, 0b0111
    orr  x3, x2, x1
    bl printf 

    ldp x29, x30, [sp], 16  
    ret 

Bitwise AND

An AND between two bits a, and b, truth table

a\b 0 1
0 0 0
1 0 1
    1001
AND 0101
--------
    0001

Example

Think of this as "masking out" bits

Bitwise AND

#include <stdio.h>

void main(int argc, char *argv[]) {
    char a = 0b1001;
    char b = 0b0111;

    printf("a=0x%x, b=0x%x\na&b = 0x%x\n", a, b, a&b);
}
fmt: .string "a=0x%x, b=0x%x\na&b = 0x%x\n"
    .balign 4
    .global main

main:
    stp  x29, x30, [sp, -16]! 
    mov  x29, sp     
    ldr  x0, =fmt           
    mov  x1, 0b1001
    mov  x2, 0b0111
    and  x3, x2, x1
    bl printf 

    ldp x29, x30, [sp], 16  
    ret 

Bitwise NOT

An AND between two bits a, and b, truth table

a 0 1
~a 1 0
NOT 1001
--------
    0110

Example

Think of this as "flipping" bits

Bitwise NOT

#include <stdio.h>

void main(int argc, char *argv[]) {
    char a = 0b1001;

    printf("a=0x%x, ~a=0x%x\n", a, ~a);
}
fmt: .string "a=0x%x, ~a=0x%x\n"
    .balign 4
    .global main

main:
    stp  x29, x30, [sp, -16]! 
    mov  x29, sp     
    ldr  x0, =fmt           
    mov  x1, 0b1001
    mvn  x2, x1               // Move and NOT
    bl printf 

    ldp x29, x30, [sp], 16  
    ret 

Bitwise XOR

An eXclusive OR between two bits a, and b, truth table

a\b 0 1
0 0 1
1 1 0
    1001
XOR 0101
--------
    1100

Example

Think of this as "a bitwise toggle" 

Bitwise XOR

#include <stdio.h>

void main(int argc, char *argv[]) {
    char a = 0b1001;
    char b = 0b0111;

    printf("a=0x%x, b=0x%x\na&b = 0x%x\n", a, b, a^b);
}
fmt: .string "a=0x%x, b=0x%x\na&b = 0x%x\n"
    .balign 4
    .global main

main:
    stp  x29, x30, [sp, -16]! 
    mov  x29, sp     
    ldr  x0, =fmt           
    mov  x1, 0b1001
    mov  x2, 0b0111
    eor  x3, x2, x1
    bl printf 

    ldp x29, x30, [sp], 16  
    ret 

Bitwise Shifts

It's also often useful to shift bits left and right in a register, for this we have >> (shift right) and << (shift left). Bits that exceed the width of the register are dropped

    1001 >> 1
--------
    0100

Example

Shift left can be used as a fast "multiply by 2" and shift right can be used as a fast "integer divide by 2" 

    1001 << 1
--------
    0010

Bitwise Shifts

#include <stdio.h>

void main(int argc, char *argv[]) {
    unsigned int a = 0b1100;
    unsigned int b = 0b1001;

    printf("a=%d, b=%d\na >> 2 = %d, b << 2 = %d\n", a, b, a >> 2, b << 2);
}
fmt: .string "a=%d, b=%d\na >> 2 = %d, b << 2 = %d\n"
    .balign 4
    .global main

main:
    stp  x29, x30, [sp, -16]! 
    mov  x29, sp     
    ldr  x0, =fmt           
    mov  x1, 0b1100
    mov  x2, 0b1001
    lsr  x3, x1, 2
    lsl  x4, x2, 2
    bl printf 

    ldp x29, x30, [sp], 16  
    ret 

Bitwise Shifts

For signed quantities, it is often important to keep the sign bit, this is what an arithmetic shift does

    1001 >> 1
--------
    1100

Example

Shift left can be used as a fast "multiply by 2" and shift right can be used as a fast "integer divide by 2" 

Bitwise Shifts

#include <stdio.h>

void main(int argc, char *argv[]) {
    unsigned int a = -1; //0b1111...111 in binary
    unsigned int b = -1; //

    printf("a=%d, b=%d\na >> 1 = %d, b << 1 = %d\n", a, b, a >> 1, b << 1);
}
fmt: .string "a=%d, b=%d\na >> 2 = %d, b << 2 = %d\n"
    .balign 4
    .global main

main:
    stp  x29, x30, [sp, -16]! 
    mov  x29, sp     
    ldr  x0, =fmt           
    mov  w1, -1
    mov  w2, -1
    asr  w3, w1, 1
    lsl  w4, w2, 1 // LSL is an Arithmetic shift
    bl printf 

    ldp x29, x30, [sp], 16  
    ret 

Binary Octal Decimal Hex
0000 0 0 0
0001 1 1 1
0010 2 2 2
0011 3 3 3
0100 4 4 4
0101 5 5 5
0110 6 6 6
0111 7 7 7
1000 10 8 8
1001 11 9 9
1010 12 10 A
1011 13 11 B
1100 14 12 C
1101 15 13 D
1110 16 14 E
1111 17 15 F

Signed Integers

Treat the highest bit as the 'sign' bit. If it's 1, then the signed integer is negative.

1010
10101010

Sign bit

If the sign bit isn't set, then we're done, and the value of the integer is the same as the unsigned integer

Signed Integers

If the sign bit is set, take the 1's complement of the number

\text{NOT} 1010
NOT1010\text{NOT} 1010
0101
01010101

Then add 1

0101
0101 0101
0110
01100110
+ 0001
+0001+ 0001

The result is then 

-110 \text{ or } -6 \text{ in decimal}
110 or 6 in decimal-110 \text{ or } -6 \text{ in decimal}

Signed Integers

Binary Octal Hexadecimal Signed Decimal
0000 0 0 0
0001 1 1 1
0010 2 2 2
0011 3 3 3
0100 4 4 4
0101 5 5 5
0110 6 6 6
0111 7 7 7
1000 10 8 -8
1001 11 9 -7
1010 12 A -6
1011 13 B -5
1100 14 C -4
1101 15 D -3
1110 16 E -2
1111 17 F -1

Next Day

Thanksgiving!

CPSC 355: Tutorial 5

By Joshua Horacsek

CPSC 355: Tutorial 5

  • 1,489