Elidrake
Elidrake

Reputation: 33

MIPS Floating Point Error

I'm attempting to figure out what exactly is going wrong with my code below.

Quick background: The idea of this program is to calculate the Batting Average and Slugging Percentage based upon the amount of Singles, Doubles, Triples, Homeruns and Outs performed by a player. One of the test cases I am running against the code has the values for these being astronomical, and whenever I attempt any sort of addition with them, the code fails. I realize that I need to use double-point floats (particularly when adding Outs and Hits, which should end up with the number 31,500,032 but instead stores 26,207,920), but I'm not at all sure how to go about working with my code to do so. Any suggestions?

# test with three batters, both average and slugging percentage
# First batter has no hits, but does have outs
# Second batter has hits and outs, with realistic values
# Third hitter has large values for some of the hits and for the
# outs. This means the hits and outs *have* to be converted from int's
# to float's in order to get the right answer.

.data

mainNumBatters:
   .word 3

mainBatter1:
   .word 0, 0, 0, 15, 0   # player with no atBats
mainBatter2:
   .word 101  # singles
   .word  22  # doubles
   .word   4  # triples
   .word 423  # outs
   .word  10  # home runs
mainBatter3:
   .word 8000000  # singles
   .word  22  # doubles
   .word 500000    # triples
   .word 23000000  # outs
   .word  10  # home runs

mainNewline:
         .asciiz  "\n"
mainBatterNumber:
         .asciiz  "Batter number: "
mainBattingAverage:
         .asciiz  "Batting average: "
mainSluggingPercentage:
         .asciiz  "Slugging percentage: "

.text

main:
         # Function prologue -- even main has one
         subu  $sp, $sp, 24      # allocate stack space -- default of 24 here
         sw    $fp, 0($sp)       # save frame pointer of caller
         sw    $ra, 4($sp)       # save return address
         addiu $fp, $sp, 20      # setup frame pointer of main

         # for (i = 0; i < mainNumBatters; i++)
         #    compute batting average
         #    compute slugging average

         la    $s0, mainNumBatters
         lw    $s7, 0($s0)       # $s7 = number of batters
         addi  $s6, $zero, 0     # $s6 = i = 0
         la    $s0, mainBatter1  # $s0 = addr of current batter's stats

mainLoopBegin:         
         slt   $t0, $s6, $s7     # $t0 = i < number of batters
         beq   $t0, $zero, mainDone

         la    $a0, mainBatterNumber
         addi  $v0, $zero, 4
         syscall
         addi  $a0, $s6, 1
         addi  $v0, $zero, 1
         syscall
         la    $a0, mainNewline
         addi  $v0, $zero, 4
         syscall

         lw    $a1,  0($s0)      # $a1 = singles
         lw    $a2,  4($s0)      # $a2 = doubles
         lw    $a3,  8($s0)      # $a3 = triples
         lw    $s5, 16($s0)      # $s5 = home runs
         lw    $s4, 12($s0)      # $s4 = outs

         sw    $s4, -4($sp)      # put outs at top of average's stack
         sw    $s5, -8($sp)      # put homeruns 2nd fm top of average's stack
         addi  $a0, $zero, 1     # $a0 = 1 = compute batting average
         jal   average

         # Print the average
         mtc1  $v0, $f12         # get result fm $v0 before we print string
         la    $a0, mainBattingAverage
         addi  $v0, $zero, 4
         syscall
         addi  $v0, $zero, 2     # print the average
         syscall
         la    $a0, mainNewline
         addi  $v0, $zero, 4
         syscall
         syscall

         # do it again for the slugging percentage
         lw    $a1,  0($s0)      # $a1 = singles
         lw    $a2,  4($s0)      # $a2 = doubles
         lw    $a3,  8($s0)      # $a3 = triples
         lw    $s5, 16($s0)      # $s5 = home runs
         lw    $s4, 12($s0)      # $s4 = outs

         sw    $s4, -4($sp)      # put outs at top of average's stack
         sw    $s5, -8($sp)      # put homeruns 2nd fm top of average's stack
         addi  $a0, $zero, 2     # $a0 = 1 = compute batting average
         jal   average

         # Print the slugging percentage
         mtc1  $v0, $f12         # get result fm $v0 before we print string
         la    $a0, mainSluggingPercentage
         addi  $v0, $zero, 4
         syscall
         addi  $v0, $zero, 2     # print the average
         syscall
         la    $a0, mainNewline
         addi  $v0, $zero, 4
         syscall
         syscall

         addi  $s6, $s6, 1       # i++
         addi  $s0, $s0, 20      # $s0 = addr of next batter's stats
         j     mainLoopBegin

mainDone:
         # Epilogue for main -- restore stack & frame pointers and return
         lw    $ra, 4($sp)       # get return address from stack
         lw    $fp, 0($sp)       # restore frame pointer for caller
         addiu $sp, $sp, 24      # restore frame pointer for caller
         jr    $ra               # return to caller

.data
printHitsOuts:
         .asciiz "Outs:     "
printHitsSingles:
         .asciiz "Singles:  "
printHitsDoubles:
         .asciiz "Doubles:  "
printHitsTriples:
         .asciiz "Triples:  "
printHitsHomeruns:
         .asciiz "Homeruns: "
printHitsNewline:
         .asciiz "\n"

.text
printHits:
         # Function prologue
         addiu $sp, $sp, -28     # allocate stack space
         sw    $fp, 0($sp)       # save frame pointer of caller
         sw    $ra, 4($sp)       # save return address
         sw    $a0, 8($sp)       # save $a0 thru $a3
         sw    $a1, 12($sp)
         sw    $a2, 16($sp)
         sw    $a3, 20($sp)
         addiu $fp, $sp, 24      # setup frame pointer of average

         # print the outs
         la    $a0, printHitsOuts
         addi  $v0, $zero, 4
         syscall
         lw    $a0, 24($sp)      # the outs are at the top of our stack
         addi  $v0, $zero, 1
         syscall
         la    $a0, printHitsNewline
         addi  $v0, $zero, 4
         syscall

         # print the singles
         la    $a0, printHitsSingles
         addi  $v0, $zero, 4
         syscall
         lw    $a0, 8($sp)
         addi  $v0, $zero, 1
         syscall
         la    $a0, printHitsNewline
         addi  $v0, $zero, 4
         syscall

         # print the doubles
         la    $a0, printHitsDoubles
         addi  $v0, $zero, 4
         syscall
         addi  $a0, $a1, 0
         addi  $v0, $zero, 1
         syscall
         la    $a0, printHitsNewline
         addi  $v0, $zero, 4
         syscall

         # print the triples
         la    $a0, printHitsTriples
         addi  $v0, $zero, 4
         syscall
         addi  $a0, $a2, 0
         addi  $v0, $zero, 1
         syscall
         la    $a0, printHitsNewline
         addi  $v0, $zero, 4
         syscall

         # print the homeruns
         la    $a0, printHitsHomeruns
         addi  $v0, $zero, 4
         syscall
         addi  $a0, $a3, 0
         addi  $v0, $zero, 1
         syscall
         la    $a0, printHitsNewline
         addi  $v0, $zero, 4
         syscall

printHitsDone:
         # Epilogue for average -- restore stack & frame pointers and return
         lw    $ra, 4($sp)       # get return address from stack
         lw    $fp, 0($sp)       # restore frame pointer for caller
         addiu $sp, $sp, 28      # restore frame pointer for caller
         jr    $ra               # return to caller

# Your code goes below this line







# $s1 = Homeruns = $f6
# Outs = $f8
# atBats = $f10
# $a1 = Singles = $f12
# $a2 = Doubles = $f14
# $a3 = Triples = $f16
# $f20 = Slugging Percentage (Not Divided)
# $f18 = Hits
# $f2 = Batting Average

average:
         # Function prologue
         addiu $sp, $sp, -56     # allocate stack space
         sw    $fp, 0($sp)       # save frame pointer of caller
         sw    $ra, 4($sp)       # save return address
         sw    $a0, 8($sp)       # 1 or 2 ; 1, batting average ; 2, slugging percentage
         sw    $a1, 12($sp)      # Number of Singles
         sw    $a2, 16($sp)      # Number of Doubles
         sw    $a3, 20($sp)      # Number of Triples
         addiu $fp, $sp, 24      # setup frame pointer of average
         sw    $s0, 28($sp)
         sw    $s1, 32($sp)
         sw    $s2, 36($sp)
         sw    $s3, 40($sp)
         sw    $s4, 44($sp)

         # Grab Outs and Homeruns from Top of Main stack
         lw    $s0, 52($sp)      # Number of Outs
         lw    $s1, 48($sp)      # Number of Homeruns

         # Convert Everything to Floating
         mtc1  $s1, $f6       # $f6 = Homeruns
         mtc1  $s0, $f8       # $f8 = Outs
         mtc1  $a1, $f12      # $f12 = Singles
         mtc1  $a2, $f14      # $f14 = Doubles
         mtc1  $a3, $f16      # $f16 = Triples


         # Calculate Hits ($f18)
         add.s $f18, $f12, $f14  # Add Singles and Doubles
         add.s $f18, $f18, $f16  # Add Triples
         add.s $f18, $f18, $f6   # Add Homeruns

         #Calculate atBats ($f10)
         add.s $f10, $f8, $f18   # Add Outs and Hits

         #Check if Batting or Slugging is to be computed
         add $s4, $zero, $zero
         addi $s4, $s4, 2
         beq $s4, $a0, averageSlugging

averageBatting:
         #Skip when atBats = 0
         mfc1 $s3, $f10
         beqz $s3, averageFinish

         #Calculate Batting Average ($f4)
         div.s $f4, $f18, $f10  # Divide Hits by atBats
         j averageFinish

averageSlugging:
         #Skip when atBats = 0
         mfc1 $s3, $f10
         beqz $s3, averageFinish

         #Calculate Slugging Average ($f0)
         add.s $f20, $f12, $f14       # $f20 = Singles + Doubles
         add.s $f20, $f20, $f14       # $f20 = Singles + Doubles*2
         add.s $f20, $f20, $f16       # $f20 = Singles + Doubles*2 + Triples
         add.s $f20, $f20, $f16       # $f20 = Singles + Doubles*2 + Triples*2
         add.s $f20, $f20, $f16       # $f20 = Singles + Doubles*2 + Triples*3
         add.s $f20, $f20, $f6       # $f20 = Singles + Doubles*2 + Triples*3 + Homeruns
         add.s $f20, $f20, $f6       # $f20 = Singles + Doubles*2 + Triples*3 + Homeruns*2
         add.s $f20, $f20, $f6       # $f20 = Singles + Doubles*2 + Triples*3 + Homeruns*3
         add.s $f20, $f20, $f6       # $f20 = Singles + Doubles*2 + Triples*3 + Homeruns*4

         div.s $f4, $f20, $f10    # Divide Hits by atBats

averageFinish:
         #Call printHits
         add $a0, $a1, $zero     # $a0 = Singles
         add $a1, $a2, $zero     # $a1 = Doubles
         add $a2, $a3, $zero     # $a2 = Triples
         add $a3, $s1, $zero     # $a3 = Homeruns
         sw  $s0, -4($sp)        # Outs at the top of printHits Stack

         jal printHits

         #Prepare for Return
         mfc1 $v0, $f4
         mtc1  $zero, $f4       # $f4 = 0

         # Epilogue for average -- restore stack & frame pointers and return
         lw    $ra, 4($sp)       # get return address from stack
         lw    $fp, 0($sp)       # restore frame pointer for caller
         lw    $a0, 8($sp)       # 1 or 2 ; 1, batting average ; 2, slugging percentage
         lw    $a1, 12($sp)      # Number of Singles
         lw    $a2, 16($sp)      # Number of Doubles
         lw    $a3, 20($sp)      # Number of Triples
         lw    $s0, 28($sp)
         lw    $s1, 32($sp)
         lw    $s2, 36($sp)
         lw    $s3, 40($sp)
         lw    $s4, 44($sp)
         addiu $sp, $sp, 56      # restore frame pointer for caller
         jr    $ra               # return to caller

Upvotes: 3

Views: 1811

Answers (1)

gusbro
gusbro

Reputation: 22585

Your problem is that you are mixing integer and floating point arithmetics. Your input numbers are represented as integers. Then you put them into floating point registers and operate with them. However you did not convert them to floating point representation.

For small numbers the addition and division works fine (you only add or divide the mantissas). However, when the numbers involved in any arithmetic operation differ in their exponent the operation will yield wrong results.

What you should do is convert the integer representation of your numbers to a floating point representation before performing floating point operations. In MIPS this is done with the cvt.s.w instruction.

So basically, you have to add one of this conversions after each mtc1 you have issued:

     # Convert Everything to Floating
     mtc1  $s1, $f6       # $f6 = Homeruns
     cvt.s.w $f6, $f6     # (convert to floating point)
     mtc1  $s0, $f8       # $f8 = Outs
     cvt.s.w $f8, $f8
     mtc1  $a1, $f12      # $f12 = Singles
     cvt.s.w $f12, $f12
     mtc1  $a2, $f14      # $f14 = Doubles
     cvt.s.w $f14, $f14
     mtc1  $a3, $f16      # $f16 = Triples
     cvt.s.w $f16, $f16

Upvotes: 2

Related Questions