CS 281 Midterm Exam Solutions -- Fall 2022 Problem #1 ========== Here's the mystery routine again, with some comments: mystery: addi $sp, $sp, 20 # Adjust stack (in wrong direction & by wrong amount) sw $a0, 0($sp) # Store $a0 (no need though) sw $t0, 4($sp) # Store $t0 (no need though) sw $s0, 8($sp) # Store $s0 (as we should) sw $s1, 12($sp) # Store $s1 (as we should) sw $s2, 16($sp) # Store $s2 (no need though) sw $ra, 20($sp) # Store $ra (no need though) move $s0, $a0 # Make two copies of the input move $s1, $a0 # srl $s1, $s1, 2 # Shift one of them right by two positions, sll $s1, $s1, 2 # ...and then back, lopping off rightmost 2 bits sub $t0, $s0, $s1 # Look at difference btw shifted and original beq $t0, $zero, rain # If no difference, go to rain li $v0, 0 # else, set up 0 as return value j snow # get ready to leave rain: li $v0, 1 # set up 1 as return value snow: addi $sp, $sp, 20 # Pop the stack (too soon though!) lw $a0, 0($sp) # Restore $a0 (needlessly) lw $t0, 4($sp) # Restore $t0 (needlessly) lw $s0, 8($sp) # Restore $s0 (as we should) lw $s1, 12($sp) # Restore $s1 (as we should) lw $s2, 16($sp) # Restore $s2 (needlessly) lw $ra, 20($sp) # Restore $ra (needlessly) jr $ra # Jump back to caller In English, it determines whether or not its first input is evenly divisible by four. If we think of it as a memory address, the procedure determines whether the input is word aligned or not. (It does that by shifting two bits to the right and then back again and looking to see if that changed the value.) Problem #2 ========== There's lots of stuff wrong: - We're using 24 bytes of stack but only allocating 20 - It adjusts the stack in the wrong direction initially - It stores registers we don't need to ($a1, $t0, $s2, $ra) - It pops the stack too soon -- should only do that after lw's Problem #3 ========== a) 10111011 --> 187 01101011 --> 107 b) 10111011 --> -128 + 59 = -69 01101011 --> 107 c) 1111 11 10111011 01101011 -------- 00100110 --> 38, regardless of whether inputs are signed or unsigned d) 111 11 10111011 10111011 -------- 01110110 Yes, whether we consider it unsigned or signed. If it's unsigned, then the bit that "fell off the left end" implies we suffered overflow. If it's signed, then we see that the sign of the result differs from the signs of the two inputs, which is also an indication of overflow. Problem #4 ========== 200x10^9 instructions, taking 850x10^9 cycles to execute on a 2.5GHz machine. a) Runtime = 850x10^9 cycles / 2.5x10^9 cycles/sec = 340 seconds b) CPI = 850x10^9 cycles / 200x10^9 inst = 4.25 cycles/instruction c) 250 seconds = 850x10^9 cycles / X ---> X = 3.4x10^9 cycles/sec = 3.4 GHz d) It used to take 340 seconds and now takes 300, so we shaved 40 seconds off of the execution. 40 seconds at 2.5GHz is 100x10^9 cycles. With each instruction replacement we save 16 cycles, so we can figure out how many replacements occurred by taking 100x10^9 / 16 --> 6.25x10^9. Problem #5 ========== a) 1 1101 011 --> -1.1101 x 2^3 = -1110.1 x 2^0 That's -14.5 in base 10. b) The sign on the exponent is what allows us to specify whether the "binary point" needs to move to the left or right. In other words, it has to do with the magnitude of the floating point value but not its sign. The sign bit, on the other hand, determines the sign of the number (but has nothing to do with its magnitude). We need them both if we want to represent all possible values. c) Having a fourth bit for the exponent means our exponent value can range from -8..+7 rather than the original -4..+3. That expands the range of magnitudes that we can represent by a factor of roughly 16, which seems like a good thing! (0 111 0111 --> 1.111x2^7 = 240.0 vs the max of 15.5 for basic BradFloats) The bad news is that we can't represent *anything* as precisely as we did before since we have fewer bits in the significand. For example, we used to be able to express 1.0001 (1 + 1/16 in base 10), but can only get to 1.001 (1 + 1/8 in base 10) now.