1
votes

I'm writing a program to add two doubles without using floating point registers. It's kind of working right now, but some numbers give me wrong results. For example 1 + 1, and 2.5 + 2.4 work, but 1+2 gives me 7, or 1001.5 + 8998.4 gives me 26383.8999 I think I have identified the problem, which is: After adding the numbers I shift(normalize) the result, which should only be the case when it's in the form for example 10.0110.The problem is I don't know how to check if the sum is 10.(something) and proceed with normalization, or 1.(something) and go straight to displaying the result. I would be most graetful if someone could help me find the answer or correct my code. EDIT: So, thanks to the help I have managed to fix the problems with addition, but now I've noticed problems with adding numbers with different signs. Well, i made it so when there are different signs numbers are substracted. It works for whole numbers, and some doubles, but for example 33.5 -23.2 results in 0.110110100110011... which is normalized to 1.10110100110011... = 27.3, while it should be 1.0110100110011... = 11.3 Updated code:

.data
text1:  .asciiz "Enter first double: "
text2:  .asciiz "Enter second double: "
text3:  .asciiz "Result: "
quest:  .asciiz "\nIf you want to continue enter 1, otherwise enter 0: "
num1a:  .word       0           # sign, exponent and part of the mantissa 
num1b:  .word       0           # second part of the mantissa
num2a:  .word       0           # sign, exponent and part of the mantissa
num2b:  .word       0           # second part of the mantissa
    .text
    .globl input    
input:  
    #print "Enter first double: "
    la  $a0, text1
    li  $v0, 4
    syscall
    # saving input double into num1
    li  $v0, 7
    syscall                 
    swc1    $f0, num1b
    swc1    $f1, num1a
    #print "Enter second double: "
    la  $a0, text2
    li  $v0, 4
    syscall
    # saving input double into num2
    li  $v0, 7
    syscall                 
    swc1    $f0, num2b
    swc1    $f1, num2a

    # loading data to registers
    lw  $t0, num1a
    lw  $t1, num1b
    lw  $t2, num2a
    lw  $t3, num2b


#########################################################sign
sign:       
    move    $t4, $t0
    andi    $t4, $t4, 0x80000000    #preserve sign, zero the rest

    move    $t5, $t2
    andi    $t5, $t5, 0x80000000    #preserve sign, zero the rest

    bne     $t4, $t5, same
    j       extract

same:
    bne     $t0, $t2, extract
    beq     $t1, $t3, zero

extract:    
################################################checking for zero
    or   $s2, $t0, $t1       #if both part of double are equal to zero we skip all the calculation
    or  $s3, $t2, $t3
    beqz    $s2, first_zero
    beqz    $s3, output

###############################sign, exponent and mantissa
    move    $t6, $t0    
    andi    $t6, $t6, 0x7FF00000    #extracting exponent to $t6 
    move    $a0, $t6

    move    $t7, $t0
    andi    $t7, $t7, 0x000FFFFF    #extracting first part of mantissa
    ori     $t7, $t7, 0x00100000    #adding prefix one to mantissa
    #remaining mantissa stays in register $t1

    move    $t8, $t2    
    andi    $t8, $t8, 0x7FF00000    #extracting exponent to $t8
    move    $t9, $t2
    andi    $t9, $t9, 0x000FFFFF    #extracting first part of mantissa
    ori     $t9, $t9, 0x00100000    #adding prefix one to mantissa
    #remaining mantissa stays in register $t3

#########################################################
exp_check:
    #beq    $t6, $t8, adding
    bgt    $t6, $t8, exp1 #exponent $t8 smaller than $t6
    bgt    $t8, $t6, exp2

    bgt     $t4, $t5, sub_first
    blt     $t4, $t5, sub_second

add:

    addu   $t7, $t7, $t9 #add first parts of mantissas
    addu   $t1, $t1, $t3 #add the rest of the mantissas

    move   $s1, $t4      #move sign of the first double to $s1

    j      shift 

sub_first:
    bgt    $t9, $t7, sub_second
    bgt    $t3, $t1, sub_second

    subu   $t7, $t7, $t9 #sub first parts of mantissas
    subu   $t1, $t1, $t3 #sub the rest of the mantissas

    move   $s1, $t4

    j      shift2 

sub_second:
    subu   $t7, $t9, $t7 #sub first parts of mantissas
    subu   $t1, $t3, $t1 #sub the rest of the mantissas

    move   $s1, $t5      #move sign of the secon double to $s1

    j      shift2 

exp1:
    sll    $s4, $t9, 31 #copy lsb of m1
    sll    $s5, $t3, 31 #copy lsb of m2

    srl    $t9, $t9, 1 #shift first part of the mantissa
    srl    $t3, $t3, 1 #shift the rest of the mantissa

    or     $t9, $t9, $s4 #put lsb in m1
    or     $t3, $t3, $s5 #put lsb in m2

    addiu  $t8, $t8, 0x00100000 #increase exponent $t8

    j      exp_check
exp2:
    sll    $s4, $t7, 31 #copy lsb of m1
    sll    $s5, $t1, 31 #copy lsb of m2

    srl    $t7, $t7, 1 #shift first part of the mantissa
    srl    $t1, $t1, 1 #shift the rest of the mantissa

    or     $t7, $t7, $s4 #put lsb in m1
    or     $t1, $t1, $s5 #put lsb in m2

    addiu  $t6, $t6, 0x00100000 #increase exponent $t6

    j      exp_check

shift:

    #andi    $t8, $t7, 0x80000000
    #li    $t4, 0
    #bne    $t8, $t4, result

    andi     $t4, $t7, 0x00200000
    beqz     $t4, result

    sll    $s2, $t7, 31 #copy least significant bit of m1
    #sll    $s3, $t1, 31 #copy lsb of m2

    srl    $t7, $t7, 1 #shift right m1
    srl    $t1, $t1, 1 #shift right m2

    or     $t1, $t1, $s2 #put m1's lsb in m2 msb
    #or     $t1, $t1, $s3 #put lsb in m2

    add    $t6, $t6, 0x00100000 #increase exp
    j result

shift2:
    andi     $t4, $t7, 0x00100000
    bnez     $t4, result

    srl     $s3, $t1, 31 #copy most significant bit of m2
    #sll    $s2, $t7, 31 #copy most significant bit of m2
    #sll    $s3, $t1, 31 #copy lsb of m2

    sll    $t7, $t7, 1 #shift right m1
    sll    $t1, $t1, 1 #shift right m2

    or     $t7, $t7, $s3 #put m2's msb in m1 lsb
    #or     $t1, $t1, $s3 #put lsb in m2

    sub    $t6, $t6, 0x00100000 #increase exp

result:
    andi   $t7, $t7, 0x000FFFFF    #preserve mantissa, zero the rest(cut the prefix - one)

    move   $t0, $s1       #copy propoer sign
    or     $t0, $t0, $t6      #add exponent
    or     $t0, $t0, $t7       #add mantissa part1
    b      output

first_zero:
    move  $t0, $t2
    move  $t1, $t3
    j     output

zero:
    li  $t0, 0x00000000
    li  $t1, 0x00000000

output:
    sw  $t0, num1a
    sw  $t1, num1b
    #print "Result: "
    la  $a0, text3
    li  $v0, 4
    syscall
    lwc1    $f12, num1b
    lwc1    $f13, num1a
    #print double - the result
    li  $v0, 3
    syscall
question:
    la  $a0, quest          #Do you want to enter new numbers or finish?
    li  $v0, 4
    syscall
    li  $v0, 5           #reads the answer (integer)
    syscall
    beq $v0, 1, input           #if input =1, continue, if 0 finish, otherwise ask again
    beqz    $v0, fin
    b   question
fin:
    li  $v0, 10             #exit
    syscall
1
Not sure what your problem is, you can simply compare the mantissa to the equivalent of 10. - Jester
Adding 1 + 1 gives 10.000... which is then shifted to 1.0000..., but 1 + 2 gives 1.100... which cannot be shifted because then I get 1.1100... = 7. I assume I need to find the way to choose which results to shift, and which not. You mean compare the first part of the mantissa (first 20 bits) with 10 or 11 and if they are equal then shift and if not then not? - CybeQ
Well, the mantissa has 53 bits, and you need to keep it that way. So all you need to test is if you get a carry which makes it 54 bits. You can do that with a simple bitwise and or a normal compare. - Jester
Isn't it 1 bit for sign, 11 bits for exponent and 52 for mantissa? Or does that 1. on the beginning also count? And it's stored in two 32 bit registers, so it's split into 20 and 32 bit parts. Is it enough to check only the first 20 bit part? So I should use bitwise 'and' between mantissa and a register with 1 on 21 bit and if it's 1 then shift it? - CybeQ
Ok, that worked. I'm using 'andi' between result's mantissa and 0x00100000 (this is where 1. should be), if it's 1 it displays the result, if it's not 1 it shifts the mantissa. Thank you Jester. - CybeQ

1 Answers

1
votes

The algorithm should be along the lines of:

Say we want to add 1234 and 567 but do it floating point style. For this not-binary example lets say we have 1234 as 1.234*10^3 and 567 is 5.67*10^2

the 10^3 is the larger exponent so we need to align the decimal places, so we shift the smaller number until the decimal lines up the bits/numbers on the lower end of the mantissa for that smaller number can fall of the end, you may want to preserve some sticky bits or not...

 1.23400*10^3
+5.67000*10^2
-------------
 1.23400*10^3
+0.56700*10^3
-------------

so now the exponents match we can add

 011000
 123400*10^3
+056700*10^3
------------
 180100*10^3

and that is the answer 1.8100*10^3

what if we had a carry?

5678 + 9876

 1 11000  
 5.67800*10^3
+9.87600*10^3
=============
15.55400*10^3

We know the size of our variables they are a little bit bigger than the mantissa (or the mantissa is broken into parts so we can have some room for the carry out, cascaded across operations).

mixing streams between binary and decimal but if you and the result with ones two above the decimal point and zeros above the decimal and to the right of the decimal. If this is not-zero then shift right one, tossing bits off the right end of the mantissa.

   15.55400*10^3
&1110.00000
===========
    1.55540*10^4
&1110.00000
===========

and now it is normalized. not necessarily efficient to do in a loop. for straight positive number addition, you can only have one extra bit to shift worst case. so you really only need to check that one bit position, if zero no shift done. but for subtraction or addition with negative numbers then you start off with a bunch of non-zero bits above the decimal point not just one. and you may have to shift left or right so you have to do a search for the first non-zero number (Well depends on if the result is positive or negative as to what to search for, assuming positive) and you may have to shift left or right left decrease the exponent for each shift, right increase the exponent. Of course do a zero check first.

Not saying you dont already know this, I didnt wade through your code. but if you were following a generic algorithm then it would be working, when for example you add a positive number with it self, then any two positives should work.

I recommend implementing in C or your favorite high level language then simply translate that to assembly if assembly is required (Is assembly required here?)