Newsgroup: it.comp.lang.perl
Titolo: Interi superiori a 32 bit

Buona Sera a tutti,
Ho scritto uno script perl, per tenere sott'occhio l'allocazione spazio su 
un server via SNMP.
Tramite alcune richieste ottengo una serie di valori e stampo a video una 
tabella.
Il problema è che il dato dimensione è spezzato in due campi (high 32 
unsinged byte and low unsigned byte), per poter gestire valori quali i 
Terabyte.
L'output video di snmpwalk stampa i valori come fossero signed, pertanto ho 
scritto la sub sig_to_uns per riconvertire opportunamente il valore.
Ho provato qualcosa come qui sotto, ma il risulato stampato è sempre un 
valore a 32 bit.
Versione perl 5.8.3.
Che cosa mi sfugge?
Perl supporta numeri a 64 bit?
Come si controlla la cosa?
I valori ricavati da snmpwalk(stringhe di testo)
32 bit bassi: "-11687552"
32 bit alti: "1"
Risultato dovrebbe essere: "
8578247040
" (opportunamente formattato in 
8180G).
Invece mi stampa: "
4283279745
" (opportunamente formattato in 4.084G)
sub sig_to_uns
{
        my $str = unpack("B32", pack("l", shift));
        $str =~ s/^0+(?=\d)//;
        return unpack("L", pack("B32", substr("0" x 32 . $str, -32)));
}
sub fmtnum
{
        ( local $numero ) = @_;
        local $contatore;
        $contatore=0;
        until ($numero <= 10000)
        {
                $contatore++;
                $numero = $numero >> 10;
        }
        $numero =~ s/(\d)(\d\d\d)/$1.$2/;
        return $numero . $unit[$contatore];
}
...codice...
$ar_size{$indice} = &sig_to_uns($valore);
...codice...
###!!!In questo punto non mi torna il funzionamento!!!!
### valori stampati 
4283279745
  = 1 << 32 |  
4283279744
$ar_size{$indice}=( &sig_to_uns($valore) << 32 ) | $ar_size{$indice};
ScriptJus 

Il problema risiede nel calcolo di (1 << 32) ed in generale
nell'utilizzo dello shift, che vengono ottimizzati in un'operazione di
basso livello. A volte una moltiplicazione può fare miracoli:
#!/usr/bin/perl
use strict;
use warnings;
# Dati di partenza
my $low = -11687552;
my $high = 1;
# 2^32 evitando noie con gli interi
my $pivot = 2 * (1 << 31);
# signed -> unsigned casting per valori a 32 bit
($_ < 0) && ($_ += $pivot) for $high, $low;
my $result = $high * $pivot + $low;
print "$high * 2^32 + $low -> $result\n";
__END__
# perl 
prova.pl
1 * 2^32 + 
4283279744
 -> 
8578247040
On 26 Feb, 23:58, "ScriptJus"


($_ < 0) && ($_ += $pivot) for $high, $low;
Sto codice mi piace!

 (opportunamente formattato in 8180G). Invece mi stampa: "
4283279745
"
Una soluzione molto elegante l'ha già postata Flavio.
Io mi limito ad illustrare una soluzione alternativa che fa uso di 
pack/unpack:
# uso: snmpcat( $low, $high )
sub snmpcat { unpack 'Q', pack 'l[2]', @_ }
print snmpcat(-11687552, 1); # 
8578247040
ed un'altra soluzione che funzionerebbe anche su un perl che *non* 
avesse il supporto per gli interi a 64-bit (s'è chiamata da sola, dato 
il thread limitrofo ;-)
sub snmpcat {
     my ($low, $high) = unpack 'L[2]', pack 'l[2]', @_;
     require Math::BigInt;
     Math::BigInt->new($high)->blsft(32)->bxor($low)
}
print snmpcat(-11687552, 1); # 
8578247040
Ciao,
-Emanuele

 Sto codice mi piace!
Sarà simpatico da leggere quando si è incuriositi, ma in generale è
poco leggibile e non dev'essere una cosa bella da trovarsi davanti
come eredità da un programmatore precedente. Sarebbe stato meglio
scrivere più per esteso:
for my $value ($high, $low) {
   if ($value < 0) { # wrap value only if negative
      $value += $pivot;
   }
}
Meno "pavone" ma sicuramente anche meno invitante a bestemmioni ed
improperi per gli eredi :)
Ciao,
   Flavio

improperi per gli eredi :)
In effetti ci ho messo qualche secondo a capirlo.
Un bel risparmio di digitazione, comunque, impensabili in altri 
ambienti/linguaggi che normalmente uso (scrivo qualcosa con vbscript o vba).
Ciao,
Paolo 


Grazie per il contributo.