MIPS: How to maintain precision when working with doubles or to round them to a specific number of decimal places?

22 Views Asked by At

I am writing a program in MIPS assembly (using MARS) that reads numbers from a .txt file and loads them into a vector. Some of these numbers are doubles. The program and its logic generally work, but I am having a problem with a few of the numbers that are not calculated correctly. For example, a number that is 0.672 in the .txt file is being stored as 0.6719999999999999. The failure occurs occasionally in additions. What are some ways to fix this?

In case you are curious, here is my code (comments are in Portuguese):

.data

fim: .double -1.0
zero: .double 0.0
dez: .double 10.0

filename: .asciiz "xtrain.txt"
quebra_linha: .asciiz "\n"
espaco: .asciiz " "

buffer: .space 17408

.align 3
vetor: .space 4000

.text
ler_arq:    # lê o arquivo
    la $a0, filename
    li $a1, 0
    li $a2, 0
    
    li $v0, 13
    syscall

load:       # carrega arquivo no buffer
    move $a0, $v0
    la $a1, buffer
    li $a2, 17408
    
    li $v0, 14
    syscall

ler_nums:   # lê byte por byte do arquivo
    la $s0, vetor
    move $s1, $s0
    la $t0, buffer
    l.d $f10, dez
    
    copy_num:
        lb $t1, 0($t0)      # carrega caracter atual em t1
        
        bne $t1, '.', cont  # se o caracter não for um ponto, pula as próximas linhas
        li $t3, 1       # se for um ponto, t3 é setado para 1 (como se fosse True)
        addiu $t0, $t0, 1
        lb $t1, 0($t0)      # avança para o próximo byte
        
        cont:
        beq $t1, ',', num_end
        beq $t1, '\r', num_end
        beq $t1, '\n', fim_copia    # se o caracter for uma vírgula ou quebra de linha, termina de copiar o número atual
        beq $t1, 0, end         # se o arquivo terminar, vai para o fim do código
        
        subu $t1, $t1, 48       # transforma ascii em int
        
        beq $t3, 1, le_double       # se t3 for True, pula para le_double
        
        le_int:
            mul $t2, $t2, 10    # multiplica o int armazenado anteriormente por 10
            add $t2, $t2, $t1   # adiciona o caracter atual
            j fim_copia     
        
        le_double:
            mtc1 $t1, $f0
            cvt.d.w $f0, $f0    # converte para double
            
            addiu $t4, $t4, 1   # contador de 10
            
            # loop para dividir o número por 10^t4
            loop_double:
                div.d $f0, $f0, $f10
                addiu $t5, $t5, 1
                blt $t5, $t4, loop_double
            
            add.d $f2, $f2, $f0
            li $t5, 0
            
            j fim_copia
        
        num_end:
            mtc1 $t2, $f4
            cvt.d.w $f4, $f4    # converte para double
            
            add.d $f4, $f4, $f2
            
            s.d $f4, 0($s1)
            
            li $t2, 0       # reinicia acumulador inteiro
            li $t3, 0       # seta t3 pra False
            li $t4, 0       # reinicia contador 10
            
            l.d, $f0, zero      # reinicia f0
            l.d, $f2, zero
            
            addiu $s1, $s1, 8   # avança posição no vetor
            
            j fim_copia
        
        fim_copia:
            addiu $t0, $t0, 1
            j copy_num

end:
    mtc1 $t2, $f0
    cvt.d.w $f0, $f0
    s.d $f0, 0($s1)     # carrega o último número no vetor
    addiu $s1, $s1, 8
    l.d $f0, fim        # carrega -1.0 no fim do vetor
    s.d $f0, 0($s1)
    l.d $f12, 0($s0)    # passa o início do vetor para f12 como parâmetro
    
    # loop para printar cada linha e número do programa
    loop:
        addiu $t9, $t9, 1
        
        li $v0, 3
        syscall
        
        la $a0, espaco
        li $v0, 4
        syscall
        
        addi $s0, $s0, 8
        l.d $f12, 0($s0)
        
        bne $t9, 8, cont_fq
        
        la $a0, quebra_linha
        li $v0, 4
        syscall
        
        li $t9, 0
        
        cont_fq:
        c.eq.d $f12, $f0
        bc1f loop
            
 

I have tried altering from double precision to single precision, and honestly I'm not sure what else to try.

0

There are 0 best solutions below