Newsgroup: it.comp.lang.perl
Titolo: Complemento a due

Salve!
Ho il problema di dover trasformare un numero esadecimale di quattro cifre
in decimale (intero) usando il complemento a due.
Sapete se esiste un comando o modulo che fa questo?
Ho provato vari utilizzi di pack e unpack ma non ottengo i risultati sperati
(forse non li uso correttamente).
Grazie.
--------------------------------
Inviato via 
http://arianna.libero.it/usenet/

 sperati(forse non li uso correttamente).
Prova a postare le tue prove di pack/unpack e vediamo se c'è qualche
problema lì...
Ciao,
-- 
+--------+ | ASCII ribbon campaign ( ) | 
www.autistici.org/kral
|  kral  | |  - against HTML email  X  |
+--------+ |              & vCards / \ | my gpg id = 0x7C946947

in decimale (intero) usando il complemento a due.
Un numero esadecimale rappresentato come da che cosa? La funzione hex non
ti va bene perché?
Ciao.

 ti va bene perché?
Devo estrarre numeri da un file binario.
Per alcuni dati uso:
read(A,$temp,2);
$temp=unpack('H*', $temp);
$temp=hex("$temp");
e ottengo i valori che trasformano ad esempio la F esadecimale in "15"
(ovviamente sopra leggo quattro lettere non una).
Per il complemento a 2 mi serve che la F diventi "-1", ma non riesco ad
ottenerlo.
Ciao


ottenerlo.
Un trucchetto potrebbe essere il pragma use integer. Mi pare comunque che
basti un and bitwise con 7fff, complemento a 1, + 1.
 
   $temp &= 0x7fff;
   $temp = ~$temp;
   ++$temp;
Non me ne intendo molto di operazioni binarie, e non ho verificato, ma
dalla definizione di complemento a 2 mi sembrerebbe corretto. Prima,
naturalmente, dovrai verificare il bit di segno (immagino con un & 0x8000).
Ciao.

 Sapete se esiste un comando o modulo che fa questo?
Potresti essere piu' chiaro con un esempio? Se intendi un semplicissimo
numero, poiche' e' una sequenza di bit, puoi vederlo come esadecimale,
decimale, ecc, semplicemente cambiando la sua formattazione con l'utilizzo
di printf al posto di print:
$a = 15; printf "%x\n", $a;
$b = 0xf; printf "%d\n", $b;
e vedi che sono lo stesso numero in memoria. Probabilmente pero' non e'
questo il lavoro/esercizio che devi fare....
bye
-- 
= active @ actisystem =
GIT d- s:+>: a-- C+++ UL++++ P++ L+++ E--- W+ N+ 
o? !K w-- O-- M- V? PS+(++) PE Y+>++ PGP++ !t 5? 
X++(+) R(+) tv+(++) b+>++ !DI D+ G e>++ h! r++ x*

 numero, poiche' e' una sequenza di bit, puoi vederlo come esadecimale,
Il problema e' che la trasformazione con i comandi normali va bene per
numeri senza segno.
Quindi per esempio in complemento a due il numero esadecimale a758
corrisponde a -22696.
Adesso proverò con i consigli di Giovanni, al massimo ritorno ad un poco
elegante ma funzionale
($temp=~ /^[89abcdef]/) ? ($temp=hex($temp)-65536) : ($temp=hex($temp));
Ciao!


 corrisponde a -22696.
Scusami, forse non mi sono spiegato. Ti chiedevo di farmi vedere con un
esempio teorico, cosa dovevi fare, che non l'ho capito :)


 esempio teorico, cosa dovevi fare, che non l'ho capito :)
Ok :)
Allora io leggo due byte (nel mio caso) in un file binario e vedo che
codifica hanno come numero binario.
Di questi il bit più significativo rappresenta il segno, gli altri il
numero; con 16 bit posso rappresentare i numeri relativi all'intervallo
-32768, +32767.
Per ottenere il decimale corrispondente devo per prima cosa vedere il bit di
segno, se e' 0 il numero rappresentato e' non negativo e lo ottengo con la
normale conversione binario-decimale.
Se invece il bit di segno e' 1 allora complemento tutti i bit e sommo 1 al
risultato (e ci caccio il meno davanti).
Esempio con 8 bit: 11100101
--> complemento ad 1= 00011010
--> aggiungo 1 = 00011011
--> trasformato e' 27
--> il numero e' quindi -27


risultato (e ci caccio il meno davanti).
O, che è lo stesso, dopo aver convertito in decimale, se il numero è
maggiore di 32767, sottrai 65536. Francamente, non so quale delle due
operazioni sia più efficiente in perl. Si può, se necessario, verificare.
Inoltre, nella documentazione del pragma use integer; si dice:
    Finally, use integer; also has an additional affect on the bitwise
    operators. Normally, the operands and results are treated as unsigned
    integers, but with use integer; the operands and results are signed.
    This means, among other things, that ~0 is -1, and -2 & -5 is -6.
Mi sembra di poter interpretare che si potrebbe usare questa caratteristica
per ottenere direttamente la conversione in interi con segno. Non ho però
verificato, e non so che vantaggi/svantaggi avrebbe rispetto ai due metodi
precedenti.
Ciao.

 -32768, +32767.
Ora sei stato chiaro! :)
Il codice basato sull'esempio del tuo post precedente e' questo:
#!/usr/bin/perl -w
$a = 0xa758;
for (my $i = 0; $i < 2; $i++) {
        # mascheramento
        my $num = ($a & (0xFFFF << (16 * $i))) >> (16 * $i);
	# ricavo il segno
        my $sign = ($num & (1 << 15)) >> 15;
	# controllo ed estensione del segno
        $num |= ($num) ? 0xFFFF0000 : 0;
        printf "%d\n", $num;
}
Nonostante i vari shift e op bitwaise e' abbastanza semplice. Non fai
altro che mascherare 16 bit alla volta, basandoti su un contatore che fa 2
giri (0 e 1). Controlli il segno (bit piu' significativo) e, se l'MSB e' 1
esegui un'estensione di segno. L'estensione di segno non e' altro che
un copiare il bit piu' significativo (in questo caso del tuo num a 16 bit)
nei restanti 16 bit della parte alta della word.
L'unico neo e' che ho dovuto utilizzare la printf, poiche' print
mi interpreta $num come unsigned int.
Spero di essere stato (+ o -) chiaro.
