mips program to calculate sin(x)

2019-09-14 11:49发布

问题:

I have this equation: Summation of sin(x)

I am trying to calculate this by creating a program on MIPS. However, it outputs incorrect BIG numbers! I have no idea where is the stupid mistake I have made. I had created three functions, one that calculates the factorial, another for the power, and the third big one is the sin function.

#read integer
li $v0, 5
syscall

addi $a0, $v0, 0 #argument of sin function, a0 = x
li $t4, 1 #n starting from 1
addi $s7, $v0, 0 #sum = x
jal sin
j end

sin:
    Loop:
    slti $t5, $t4, 5 #t0 < 5 (n < 5)
    beq $t5, 0, exitLoop
    addi $sp, $sp, -8 # adjust stack for 2 items
    sw $ra, 4($sp) # save return address
    sw $a0, 0($sp) # save argument
    li $a0, -1 #argument of pow function, number = -1
    addi $a1, $t4, 0 #argument of pow function, power = t4
    jal power
    addi $s0, $v0, 0 #s0 = v0 (return value of pow)
    lw $a0, 0($sp) # restore original x

    sll $a1, $t4, 1 #a1 = 2n 
    addi $a1, $a1, 1 #a1 = 2n+1
    jal power
    addi $s1, $v0, 0 #s0 = v0 (return value of pow)

    sll $a0, $t4, 1 #a0 = 2n
    addi $a0, $a0, 1 #a0 = 2n + 1
    jal factorial
    addi $s2, $v0, 0 #s0 = v0 (return value of pow)
    lw $a0, 0($sp) # restore original n
    lw $ra, 4($sp) # and return address

    mult $s0, $s1 # LO = (-1)^n * x^(2n+1)
    mflo $s3 # S3 = LO
    div $s3, $s2 # s3 / (2n+1!)
    mflo $s3
    add $s7, $s7, $s3 #sum = sum + s3
    addi $t4, $t4, 1 #n++

    j Loop  
    exitLoop:
addi $v1, $s7, 0
addi $sp, $sp, 8
jr $ra

power:
    addi $t0 $a0, 0 #t0 = a0
    li $t1, 0 #i = 0
    loop:
    slt $t3, $t1, $a1 #if i < n
    beq $t3, 0, exit 
    mult $t0, $a0 #t0 * a0
    mflo $t0 #LO = t0
    addi $t1, $t1, 1
    j loop
    exit:
    addi $v0, $t0, 0
    jr $ra

factorial:
    li $t0, 1
    bgt $a0, $t0, L1
    li $v0, 1
    jr $ra
    L1:
    addi $sp, $sp, -8 # adjust stack for 2 items
    sw $ra, 4($sp) # save return address
    sw $a0, 0($sp) # save argument
    addi $a0, $a0, -1 # decrement n
    jal factorial # recursive call
    lw $a0, 0($sp) # restore original n
    mul $v0, $a0, $v0 # multiply to get result
    lw $ra, 4($sp) # and return address
    addi $sp, $sp, 8 # pop 2 items from stack
    jr $ra # and return
end:
li $v0, 1
addi $a0, $v1, 0
syscall

回答1:

As Jester mentioned, you should use floating point arithmetic.

You can also greatly simplify things by taking advantage of the fact that each power term and each factorial term in the series is a simple delta from the previous one. No need to recalculate from scratch and no need to calculate factorial recursively [it's much easier to use a loop].

No need to have separate functions at all. Here's the function in C. This is a relatively easy translation to asm:

double
qsin(double x)
{
    double x2;
    double cur;
    int neg;
    double xpow;
    double n2m1;
    double nfac;
    int iters;
    double sum;

    // square of x
    x2 = x * x;

    // values for initial terms where n==0:
    xpow = x;
    n2m1 = 1.0;
    nfac = 1.0;
    neg = 1;

    sum = 0.0;

    // NOTES:
    // (1) with the setup above, we can just use the loop without any special
    //     casing
    // (2) this _will_ do an unnecessary calculation [that gets thrown away] on
    //     the last iteration, but it's a tradeoff for simplicity
    // (3) when translated to asm, it should be easy to restructure to avoid
    //     this (i.e. just harder to express in a loop here)
    for (iters = 5;  iters > 0;  --iters) {
        // calculate current value
        cur = xpow / nfac;

        // apply it to sum
        if (neg < 0)
            sum -= cur;
        else
            sum += cur;

        // now calculate intermediate values for _next_ sum term

        // get _next_ power term
        xpow *= x2;

        // go from factorial(2n+1) to factorial(2n+1+1)
        n2m1 += 1.0;
        nfac *= n2m1;

        // now get factorial(2n+1+1+1)
        n2m1 += 1.0;
        nfac *= n2m1;

        // flip sign
        neg = -neg;
    }

    return sum;
}


标签: assembly mips