Conta gli 1 di un numero binario

di Vincenzo.79 il
11 risposte

Di seguito posto una traccia d'esame che sono riuscito a svolgere. 

Sperando di aver rispettato la traccia, ho solo un dubbio nella funzione perchè se inserisco il comando "# lw ed sw ra, 0(sp)" mi da errore

#########################################################################################################
# Scrivere un programma che legga un numero da tastiera e ne stampi il numero di 1 contenuto			#
# nella sua rappresentazione binaria utilizzando una funzione (count_ones) che riceve in input			#
# il puntatore all'intero e ritorna il conteggio degli "1", che sarà successivamente stampato nel		#
# main. La chiamata della funzione dovrà rispettare le convenzioni per lo scambio dei parametri			#
# - ritorno dei risultati e per il salvataggio dei registri.											#
# ESEMPIO: se il numero inserito è 17892 (in binario --> 0100010111100100), in numero di 				#
# "1" dovrà essere 7.																					#
#########################################################################################################

.globl _main

.data
	input_num:			.asciz " Dammi un numero (-2^31 <= x < 2^31): "
	output_num:			.asciz " Il numero in binario: "	
	msg_count:			.asciz "\n Il numero di 1 nella rappresentazione binaria è "
	
	resto: 				.asciz " Il resto è "
	divisione: 			.asciz " La divisione è "
	new_line: 			.asciz "\n"
	
.text
_main:
	
	la a0, input_num
	li a7, 4
	ecall
	li a7, 5
	ecall
	mv t1, a0
	
	li t2, 2							# Valore per la divisione
	
loop:
# Calcolo il resto	
	rem t3, t1, t2						# Calcolo il resto e lo salvo nel registro t3
print_resto:	
	la a0, resto
	li a7, 4
	ecall
	mv a0, t3
	li a7, 1
	ecall
	
# Calcolo la divisione	
	div t1, t1, t2
print_divisione:	
	la a0, divisione
	li a7, 4
	ecall
	mv a0, t1
	li a7, 1
	ecall
	
# New_Line
	la a0, new_line
	li a7, 4
	ecall	

# Codice per contare gli 1	
	beqz t3, loop
	jal ra, count_ones
	beqz t1, end
	j loop
	
end:	
	la a0, msg_count
	li a7, 4
	ecall
	mv a0, s0
	li a7, 1
	ecall
	
# Fine del programma
li a7, 10
ecall

# Funzione per contare gli 1 nel numero in binario
count_ones:
	addi sp, sp, -8
#	lw ra, 0(sp)
	lw s0, 4(sp)

	addi s0, s0, 1
	
	sw s0, 4(sp)
#	sw ra, 0(sp)
	addi sp, sp, 8
	ret

11 Risposte

  • Ma sei sicuro della funzione? In primis, sposti lo stack pointer di -8 e poi di +16... Così mandi tutto in vacca. XD

    Poi non vedo da nessuna parte il loop della divisione per 2... Come lo "converti" in binario il numero intero?

  • 19/08/2026 - Sgrubak ha scritto:

    Ma sei sicuro della funzione? In primis, sposti lo stack pointer di -8 e poi di +16... Così mandi tutto in vacca. XD

    Poi non vedo da nessuna parte il loop della divisione per 2... Come lo "converti" in binario il numero intero?

    Ho risposto modificando il codice del messaggio iniziale

  • 19/08/2026 - Vincenzo.79 ha scritto:

    Ho risposto modificando il codice del messaggio iniziale

    Eh... Ma continuo a non vedere il loop per il conteggio. La consegna dell'esercizio dice:

    una funzione (count_ones) che riceve in input il puntatore all'intero e ritorna il conteggio degli "1"

    Tu al momento fai solo incrementare un registro nella funzione, ma di fatto non stai contando nulla. La funzione deve avere in a0 il puntatore all'intero da "analizzare", quindi convertirlo per contare gli 1 e rimettere in a0 il risultato.

    Il main quindi dovrà mettere in a0 l'intero, richiamare la funzione ed infine stampare il conteggio.

    19/08/2026 - Vincenzo.79 ha scritto:

    se inserisco il comando "# lw ed sw ra, 0(sp)" mi da errore

    Ma che comando è?

    EDIT: attento che nella funzione hai invertito sw ed lw... Prima scrivi in memoria i valori dei registri sX con l'istruzione sw, poi li ripristini all'uscita della funzione con lw.

  • 20/08/2026 - Sgrubak ha scritto:

    Tu al momento fai solo incrementare un registro nella funzione, ma di fatto non stai contando nulla. La funzione deve avere in a0 il puntatore all'intero da "analizzare", quindi convertirlo per contare gli 1 e rimettere in a0 il risultato.

    Il main quindi dovrà mettere in a0 l'intero, richiamare la funzione ed infine stampare il conteggio.

    Prima di tutto ti ringrazio; 

    corretto...vediamo se ho fatto bene

    ################################################################
    # Scrivere un programma che legga un numero da tastiera e ne stampi il numero di 1 contenuto			
    # nella sua rappresentazione binaria utilizzando una funzione (count_ones) che riceve in input				
    # il puntatore all'intero e ritorna il conteggio degli "1", che sarà successivamente stampato nel				
    # main. La chiamata della funzione dovrà rispettare le convenzioni per lo scambio dei parametri			
    # - ritorno dei risultati e per il salvataggio dei registri.																				
    # ESEMPIO: se il numero inserito è 17892 (in binario --> 0100010111100100), in numero di 				
    # "1" dovrà essere 7.																																#################################################################
    
    .globl _main
    
    .data
    	input_num:			.asciz " Dammi un numero (-2^31 <= x < 2^31): "
    	msg_count:			.asciz " Il numero di 1 nella rappresentazione binaria è "
    		
    .text
    _main:
    	
    	la a0, input_num
    	li a7, 4
    	ecall
    	li a7, 5
    	ecall
    	mv s0, a0							# Numero inserito dall'utente
    	
    	jal ra, count_ones
    	
    end:
    	la a0, msg_count
    	li a7, 4
    	ecall
    	mv a0, s1
    	li a7, 1
    	ecall
    	
    # Fine del programma
    li a7, 10
    ecall
    
    # Funzione per contare gli 1 nel numero in binario
    count_ones:
    	addi sp, sp, -16
    	sw ra, 0(sp)					# registro di ritorno
    	sw s0, 4(sp)					# numero inserito dall'utente
    #	sw s1, 8(sp)
    	
    	li t0, 2						# registro divisore
    loop:
    # Resto della divisione, serve per la rappresentazione binaria del numero inserito	
    	rem t1, s0, t0
    # Divisone del numero inserito
    	div s0, s0, t0
    	
    	beqz t1, other_loop
    	addi s1, s1, 1					# conteggio degli 1 presenti nel numero binario
    other_loop:	
    	bnez s0, loop					# ciclo per terminare la divisione
    	
    #	lw s1, 8(sp)
    	lw s0, 4(sp)
    	lw ra, 0(sp)
    	addi sp, sp, 16
    	ret
    
    

    Ora l'unica che devo chiarire è questa: nella funzione ho dichiarato anche la variabile s1, che in effetti è il contatore degli 1 presenti nel numero binario; ebbene tale variabile non va salvata e caricata nello stack???

    Anche perchè se lo faccio (codice evidenziato nella funzione) dopo azzero la variabile e nella stampa nel main ottengo sempre 0...

  • 25/08/2026 - Vincenzo.79 ha scritto:

    vediamo se ho fatto bene

    Per come la vedo io (che sono studente autodidatta come te) ora l'esercizio è più centrato: il main prepara ed invoca la funzione, la funzione fa il suo, ed il main stampa.

    Quel che per me ancora si può aggiustare è che continui a non rispettare la convenzione per la quale il parametro in ingresso sia nel registro a0.

    Anche il salvataggio di ra sullo stack non lo vedo funzionale. La funzione è foglia... Al suo interno non ci sono quindi altre modifiche ad ra, allora perché spostarlo e rimetterlo quando resta invariato per tutta l'esecuzione?

    Alla luce di ciò, proprio perché è una funzione foglia, credo sia superfluo l'uso dei registri sX al suo interno. Usa i tX e quel che succede succede. Se nel main stai usando registri tX ed i relativi valori ti servono che permangano dopo l'invocazione della funzione (ma non è il caso), allora sposta tutto sugli sX. I tX in quel contesto non van bene.

    Dopo questi ragionamenti, viene automatico comprendere che la tua preoccupazione per s1 è vana, e per due motivi:

    1. Perché usare un registro sx in una funzione foglia?
    2. il valore di ritorno va comunque in a0

    Un ultima considerazione sull'algoritmo: tu fai "manualmente" tramite divisione e resto, la conversione in numero binario, ma tu nel registro hai già un numero binario. La divisione per 2 in binario equivale allo shift a destra dei bit.

    Io ero arrivato a risolvere l'esercizio con la funzione

    count_ones:
    # input: a0, numero in cui contare i bit a 1
    # output: a0, il conteggio dei bit
    # t0, contatore per il ciclo
    # t1, memorizza l'ultimo bit a destra dopo lo shift
    # t2, contatore per i bit a 1
    
    bez a0, zero, ret_zero      # se a0 = 0 ritorno zero
    
    li t0, 32                   # Inizializzo il contatore per il ciclo
    
    loop_count:
    andi t1, a0, 1              # Ottengo l'ultimo bit a destra
    add t2, t2, t1              # Aggiungo l'ultimo bit 
    addi t0, t0, -1             # decremento il contatore
    srli a0, a0, 1              # effettuo lo shift a destra dei bit (divido per 2)
    bnez t0, loop_count         # ciclo fino a che il contatore non è 0
    
    mv a0, t2                   # rendo disponibile il risultato all'esterno della funzione
    
    ret
    
    ret_zero:
    li a0, zero
    ret

    poi ho chiesto all'IA e si può fare molto meglio. Questo algoritmo è ulteriormente ottimizzabile e ne esistono altri (ai quale non sarei mai arrivato da solo) che rendono il ciclo sui bit un macigno al confronto.

  • 25/08/2026 - Sgrubak ha scritto:

    ai quale non sarei mai arrivato da solo

    Basta fare una cosa che, comprendo, e' diventata "obsoleta" e cioe "STUDIARE"!!!!

    :-)

    E comunque il codice NON E' "molto meglio", si puo' fare di MOOOOOOOOOLTO meglio :-).

    Spoiler: fare uno shift di 8 bit in un colpo solo.

  • 29/08/2026 - migliorabile ha scritto:

    e' diventata "obsoleta" e cioe "STUDIARE"!!!!

    Hei, è per colpa tua se ho The Art of Computer Programming, vol 1 sul comodino. :-D

    Ci avevo provato, ma tra la matematica ed il MIXAL mi aveva un po' segato le gambe.

    Adesso che ho approcciato al RISC-V, posso riprenderlo. Salterò sempre la parte della matematica (a malincuore), ma lo faccio per passione. Mi può bastare. XD

    29/08/2026 - migliorabile ha scritto:

    fare uno shift di 8 bit in un colpo solo

    Mi incuriosisce l'approccio. Io era arrivato a trovare (e capire, anche se c'è voluto un attimo) il popcount di Kernighan che mi sembrava già tanta roba.

  • L'approccio e' banale:

    vettore di 256 element ogn'uno contenente il numero di bit a 1 per un intero corrispondente al'indice:

    0->0

    1->1

    2->1

    3->2

    ...

    254->6

    255->7

    quindi prendi un byte alla volta, accedi al vettore e aggiungi il numero corrispondente

    soluzione semplicissima ed un improvement di 10 volte in velocita'

    (piu o meno)

    NB: io TAoCP li ho tutti!

    Fonte inesauribile di idee.

    Mi sa che Knuth tirera' le cuoia prima di finire di scrivere tutti i volumi :-)

  • 03/09/2026 - migliorabile ha scritto:

    e' banale:

    Eh... Si, ma serve arrivare a pensarlo. XD

    Se ho ben compreso, i passaggi sarebbero:

    1. r1 = numero and 11111111;
    2. r2 = r2 + vettore[r1];
    3. numero=numero>>>8 ;
    4. loop se numero non è 0
    5. ritorno r2.

    Io continuando a studiare l'argomento popcount avevo anche trovato la somma tramite bitmask che però devo ancora studiare bene. Ho capito a grandi linee come funziona ma qualche passaggio ancora mi fugge.

    Faccio un tentativo finto erudito: Kernighan è O(n) sul peso di Hamming, il tuo è O(n) sul numero di byte mentre l'approccio con la bitmask è sempre O(1).È corretta come affermazione?

    03/09/2026 - migliorabile ha scritto:

    io TAoCP li ho tutti!

    Ho visto che ce ne sono un po'. Ma mi sa che me li gusterò alla pensione. XD

  • O(1) vorrebbe dire che con un'unica maschera, in un'unica step, tiri fuori il numero di bit a 1.

    Usi un'unica maschera?

    :-)

    Quasi tutto giusto :-)

    Guarda_ti/tevi questo:

    https://www.jjj.de/fxt/

  • 04/09/2026 - migliorabile ha scritto:

    O(1) vorrebbe dire che con un'unica maschera, in un'unica step, tiri fuori il numero di bit a 1.

    Io avevo inteso che la notazione O(1) identificasse quegli algoritmi che, indipendentemente dall'input, mantengono l'esecuzione costante.

    04/09/2026 - migliorabile ha scritto:

    Guarda_ti/tevi questo:

    Non c'ho capito un'acca...  ç_ç Mi serve altro studio. Tanto altro studio.

    Ma nel mio piccolo ho sfruttato l'idea del PopCount in un mio progetto C#. Ci sto ancora lavorando, ma già le prime soddisfazioni arrivano. Serve sempre sapere tutto, non sai mai quando può servirti.

    Dannata cultura... XD

Devi accedere o registrarti per scrivere nel forum
11 risposte