CPSC 355: Tutorial 6
Binary Arithmetic
PhD Student
Fall 2017
Outline
- Assignment 1 breakdown
- Errata for last tutorial
- Binary Addition
- Binary Multiplication
- Tips for Assignment 2
Assignment 1
Generally very well done : )
A handful of students missed the pre-test loop with a comparison at the bottom of the loop for part b.
// setup initial regiters here...
b loop_test //branch to loop test
loop_top: // compute -4x^3 -27x^2 + 5x + 45
// check if max etc...
// increment x_r
loop_test: cmp x_r, 6 //test x with 6
b.lt loop_top //go to top of loop
Assignment 1
.global and .balign statements are misunderstood
fmt_string: .string "String 1"
.global main
.balign 4
fmt_string2:.string "String 2"
.global main
.balign 4
main:
// code goes here
fmt_string: .string "String 1"
fmt_string2:.string "String 2"
.global main
.balign 4
main:
// code goes here
What this should look like
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[]) {
int a = -1; //0b1111...111 in binary
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
Arithmetic in binary
As it turns out, all the binary operations shown up until now are easy to implement in transistor logic.
With a few transistors it's easy to build an AND, OR, XOR operator -- but how do we add numbers?
Basically, we have to figure out how to use binary operations to perform arithmetic
Addition in binary
Let's start with an 8-bit example, binary addition works the same as base-10 addition
Addition in binary
Moving on to a more general case
As it turns out, we can very easily express the results and carries
Assuming
This was c_i-1 in the video, but it should be c_i
Addition in binary
#include <stdio.h>
int main()
{
unsigned int a, b, result = 0;
unsigned int carry = 0, i = 0;
a = 100;
b = 123;
printf("%d + %d = \n", a, b);
for(i = 0; i < 32; i++)
{
int ai = a & 0x1;
int bi = b & 0x1;
result = result >> 1;
if(ai ^ bi ^ carry) {
result |= 0x80000000;
} else {
result &= 0x7FFFFFFF;
}
carry = ((ai ^ bi) & carry) | (ai & bi);
a = a >> 1;
b = b >> 1;
}
printf("%d\n", result);
}
Addition in binary
#include <stdio.h>
int main()
{
unsigned int a, b, result = 0;
unsigned int carry = 0, i = 0;
a = -100; // <---- This is now negative
b = 123;
printf("%d + %d = \n", a, b);
for(i = 0; i < 32; i++)
{
int ai = a & 0x1;
int bi = b & 0x1;
result = result >> 1;
if(ai ^ bi ^ carry) {
result |= 0x80000000;
} else {
result &= 0x7FFFFFFF;
}
carry = ((ai ^ bi) & carry) | (ai & bi);
a = a >> 1;
b = b >> 1;
}
printf("%d\n", result);
}
Multiplication in binary
Let's do another example, with an unsigned 4-bit number. Unsurprisingly, this works like base-10 arithmetic
Multiplication in binary
This is a little trickier to write out a recurrence relation between bits as we did before, but it's easy to see how to write an algorithm that does this.
Multiplication in binary
#include <stdio.h>
int main()
{
int multiplier, multiplicand, result_high, result_low, i, negative;
long int result, temp1, temp2;
multiplicand = -8192;
multiplier = 99;
result_high = 0;
result_low = 0;
printf("multiplier = 0x%08x (%d) multiplicand = 0x%09x (%d) \n\n",
multiplier, multiplier, multiplicand, multiplicand);
negative = multiplier < 0;
for(i = 0; i < 32; i++) {
if(multiplier & 1) {
result_high = result_high + multiplicand;
}
multiplier = multiplier >> 1;
result_low = result_low >> 1;
if(result_high & 1) {
result_low = result_low | 0x80000000;
} else {
result_low = result_low & 0x7FFFFFFF;
}
result_high = result_high >> 1;
}
if(negative) {
result_high = result_high - multiplicand;
}
printf("result_high = 0x%08x, result_low = 0x0%08x\n", result_high, result_low);
temp1 = (long int) result_high;
temp1 = temp1 << 32;
temp2 = (long int) result_low & 0xFFFFFFFF;
result = temp1 + temp2;
printf("64-bit result: 0x%016lx (%ld)\n", result, result);
}
Sign extension
temp1 = (long int) product;
temp1 = temp1 << 32;
temp2 = (long int) multiplier & 0xFFFFFFFF;
result = temp1 + temp2;
Problem: temp1 is a long register (Xn) and product is a word register (Wd).
Luckily, there's an instruction that keeps the value of a word length register, and extends its sign bit
sxtw temp1, product // Sign extend product, and put it in temp1
Tips for Debugging
for(i = 0; i < 32; i++) {
if(multiplier & 1) {
product += multiplicand;
}
multiplier = multiplier >> 1;
if(product & 1) {
multiplier = multiplier | 0x80000000;
} else {
multiplier = multiplier & 0x7FFFFFFF;
}
product = product >> 1;
// print out product and multiplier every iteration of the loop
printf("product = 0x%08x, multiplier = 0x0%08x\n", product, multiplier);
}
CPSC 355: Tutorial 6
By Joshua Horacsek
CPSC 355: Tutorial 6
- 1,528