MIPS silnia

0

Muszę napisać program obliczający silnię dla dużych liczb (np. 200). Mam taki kod:

 .text
    main:
    li $v0, 4
    la $a0, prompt
    syscall

    li $v0, 5
    syscall
    move $s0, $v0
    move $t1, $v0
    li $t0, 1
    loop:
    mul $t0, $t0, $s0

    addi $s0, $s0, -1
    bgtz $s0, petla

    li $v0, 1
    move $a0, $t0
    syscall

    li $v0, 10
    syscall

    .data
    prompt: .asciiz "\nGive a number: " 

Program nie daję rady jednak policzyć silni już dla liczb większych od 31. Ktoś ma jakiś pomysł, jak to naprawić?

0

Większych od 31? Chyba żartujesz, ten program nie policzy silni dla liczb większych niż 13 (13! = 6227020800, 2^32 = 4294967296. /większej liczby nie upchniesz w rejestsze/).

Silnia z 31 to 8222838654177922817725562880000000, a silnia z 200 wynosi:
78865786736 47905035523 63213932185 06229513597 76871732632 94742533244 35944996340 33429203042 84011984623 90417721213 89196388302 57642790242 63710506192 66249528299 31113462857 27076331723 73998894392 24456214516 64240254033 29186413122 74282948532 77524242407 57390324032 12574055795 68660226031 90417032406 23517008587 96178922222 78962370389 73747200000 00000000000 00000000000 00000000000 0000000000
powodzenia w próbie zmieszczenia tego w 32bitowym rejestrze :D

Ok, to może coś konstruktywnego. Jeśli masz tylko policzyć tę silnię, to nie jesteś jeszcze na straconej pozycji.
Mul $d, $s, $t w MIPS którego używasz to nie opkod tylko konwencjonalna pseudoinstrukcja będąca aliasem do

mult $s, $t
mflo $d

mult $s $t mnoży $s * $t i 64bitowy wynik wrzuca do specjalnych rejestrów $hi:$lo które można odczytać za pomocą specjalnych instrukcji mfhi i mflo.

Teraz pomysł jak wyliczyć tę silnię:
Twoją silnię najprościej chyba reprezentować w pamięci jako ciąg liczb 32bitowych albo 16bitowych (formalnie, zapisać ją w postaci liczby w systemie o podstawie 65536, ale taka definicja Ci nie pomoże wiele ;) ).
i dalej mnożysz jak na kartce, majac liczbę A B C D (gdzie A..D to liczby w systemie o podstawie N) którą chcesz pomnożyć przez x, zaczynasz od pomnożenia D przez x, dodajesz do następnej komórki pamięci (Dx)/N a obecna cyfra to (Dx)%N. Jako że zdaję sobie sprawę że z tego wytłumaczenia nic nie wynika, wyjaśnienie na przykładzie N=10 (zwykły system dziesiętny):

Mnożenie 1234 * 6
Zaczynasz od ostatniej cyfry = 4
4 * 6 = 24. 24/10 = 2 a 24%10 = 4. Czyli ostatnia cyfra wyniku to 4 a 2 przechodzi dalej.
3 * 6 = 18. Dodajmy 2 które przeszło, czyli mamy 20. 20/10 = 2 a 20%10 = 0. Czyli kolejna cyfra to 0, a 2 przechodzi dalej.
2 * 6 = 12. Dodajemy 2, czyli 14. 14 / 10 = 1 czyli przechodzi 1, 14%10 = 4, czyli kolejna cyfra to 4.
1 * 6 = 6. Dodajemy 1, czyli 7. 7/10 to 0 czyli nic nie przechodzi, 7%10 to 7 czyli kolejna cyfra to 7.
Biorąc wszystkie cyfry, wynik to 7404, co zgadza się z tym co podpowiada matematyka.

No i w zasadzie na tym polega arytmetyka dużych liczb (hasło do googlowania po więcej przykładów). W sumie możesz to nawet liczyć na tych cyfrach dziesiętnych zamiast kombinować z podstawą 2^32/2^16, będzie dużo łatwiej wypisać wynik (uwierz mi, implementowałem już dzielenie liczb 64bitowych przez 10 w MIPS i nie było łatwo. Co dopiero dowolnie dużych).

Cóż, prosto nie będzie, szczególnie w asemblerze.

edit bo oczywiście wypisanie tego to też będzie problem. Musisz wypisywać znak po znaku, dlatego chyba najprościej to zrobić na liczbach dziesiętnych. Pomyśl jak byś wykonywał to mnożenie na kartce i zrób to samo w asemblerze).

1 użytkowników online, w tym zalogowanych: 0, gości: 1