Search code examples
assemblyfibonacciriscvinteger-overflow

Fibonacci Function in RISC V


Implementing the fibonacci function using RISC - V, so that f(0) = 0 , f(1) = 1, ..., up to f(47). My output matches everything up to 46. But when i try to calculate f(47) i get: -1323752223.
Here's the output:Output from Code below with n=47

Is there some sort of Overflow because i get a negative Integer value? Where should i look into to try and fix the error?

.data
    n:  .word 47
.text
.globl main

main:

            li x2, 0                # Used to determine if n (x7) equals 0
            li x3, 1                # Used to determine if n (x7) equals 1
            li x5, 0                # First number
            li x6, 1                # Second number
            lw x7, n                # Limit
            li x8, 1                # Counter
            beq x7, x2, DO          # If n == 0 then jump to DO (Which shoud print 0). Implements f(0) = 0
            beq x7, x3, WRITE       # if n == 1 then jump to WRITE (Which should print 1). Implements f(1) = 1  

    LOOP:   beq x8, x7, EXIT        # Comparse the counter x8 which starts with 1 to n (limit). If x8 == x7 jump to EXIT
            add x4, x5, x6          # Add x5 to x6 and store in x4          
            ori x5, x6, 0           # Assign the second number to my first number
            ori x6, x4, 0           # Assign the sum of x5 and x6 to my second number
            addi x8, x8, 1          # Add 1 to my counter
            j LOOP                  # Jump to loop

    EXIT:   
            li x17, 1               # Load constant 1 to x17
            add x10,x4,x0           # Add x4 (which contains the result after the above coe) to x10
            ecall                   # Issue an SystemCall which prints an integer (Because of the 1 in x17)
            li x17, 5                                   
            ecall
            li x17, 10      
            ecall                   # Reads an int from input console (Because of the 10 in x17)
    DO:   
            li x4, 0                # load 0 in x10 (x10 will be used by the SysCall to print) and print            
            add x10,x4,x0       
            li x17, 1
            ecall   
            li x17, 5
            ecall
            li x17, 10
            ecall    

    WRITE:  li x4, 1                # load 1 in x10 and print
            add x10,x4,x0
            li x17,1
            ecall
            li x17, 5
            ecall
            li x17, 10
            ecall

Solution

  • Yes, you have found the boundary of what signed 32-bit integers can hold.

    • fib(47) = 2971215073 won't fit in 32-bit integers as signed, but will fit as unsigned — however, RARS does not have an "unsigned int" print function.

    • fib(48) = 4807526976 won't fit in 32-bit form, even as unsigned.

    List of fibonacci numbers: https://www.math.net/list-of-fibonacci-numbers


    If you want to represent larger numbers, you will need a strategy.

    • if precision is not important, you can switch to floating point, where the results with such large numbers will be inexact.

    • use two integers together for 64-bit arithmetic — you'll be good up to fib(92) = 7540113804746346429.

    • use variable length integers for precision limited only by computer memory and compute time

    • a complex combination of the above


    And finally, you can detect and issue an error on overflow of your chosen arithmetic data type.  Overflow detection on RISC V is somewhat simple but not really obvious.

    Technically, addition of 2 arbitrary 32-bit numbers results in a 33 bit answer — but no more than 33 bits, so we can use that knowledge of math in detecting overflow.

    First, in a 32-bit number the top bit is either the sign bit (when signed data type), or the magnitude bit (when unsigned data type).

    If you add two positive numbers (as is the case with fib) and the sign bit is set, you have signed overflow — but a proper bit pattern, if interpreted as unsigned.  However, you won't be able to print the number properly using the ecall #1, because it will print the number as signed and interpret as negative.  You can write your own unsigned number print; you can also look for this case and simply stop the program from printing and issue an error instead.

    Going beyond that you can check for overflow in 32-bit unsigned addition by seeing if the resulting value is less than one of the input operands.

    The last approach is also used in multiword addition to make a carry from the lower word(s) to higher word(s).