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.