2010-10-06 11 views
5

Ho bisogno di fare un po 'di aritmetica con grandi numeri esadecimali qui sotto, ma quando provo a fare output ricevo messaggi di errore di overflow "Numero esadecimale> 0xffffffff non-portatile", messaggi su non portatile o il valore esadecimale massimo a 32 bit FFFFFFFF.Come posso eseguire l'aritmetica esadecimale/decimale a 64 bit E produrre un numero intero in HEX come stringa in Perl?

Tutto ciò implica che la lingua standard e le routine di output soddisfano solo i valori a 32 bit. Ho bisogno di valori a 64 bit e ho fatto molte ricerche, ma non ho trovato nulla che BOTH abilita l'aritmetica E emette il grande numero in esadecimale.

my $result = 0x00000200A0000000 + 
      (($id & 0xFFFFF) * 2) + (($id/0x100000) * 0x40000000); 

Così, per $ id con i seguenti valori dovrei ottenere $result:

$id = 0, $result = 0x00000200A0000000 
$id = 1, $result = 0x00000200A0000002 
$id = 2, $result = 0x00000200A0000004 

Come posso fare questo?

Ecco i miei risultati di ricerca inconcludenti, con ragioni:

  • How can I do 64-bit arithmetic in Perl?

  • How can I sum large hexadecimal values in Perl? Vague, risposta non definitivamente preciso e nessun esempio.

  • Integer overflow non conclusiva

  • Integer overflow non conclusiva

  • bigint nessuna info su assegnazione, l'aritmetica o uscita

  • bignum esempi non è vicino al mio problema.

  • How can I sprintf a big number in Perl? esempio dato non è abbastanza informazioni per me: non trattare con esadecimale assegnazione o l'aritmetica.

  • Re: secret code generator Alcuni esempi che utilizzano Fleximal, menziona to_str al valore della produzione di variabile, ma 1) non vedo come è stato assegnato alla variabile e 2) ottengo errore "Impossibile chiamare il metodo" to_str " senza un pacchetto o oggetto riferimento" quando eseguo il mio codice utilizzando esso.

  • String to Hex Esempio di utilizzo di Math :: BigInt che non funziona per me - ancora ottenere errore di overflow.

  • Is there a 64-bit hex()? Ci siamo quasi - ma non si occupa emettere il gran numero in esadecimale, essa parla solo di decimali.

  • CPAN Math:Fleximal fa l'aritmetica, ma non sembra essere qualsiasi mezzo effettivamente uscita il valore esadecimale ancora in

  • sprintf sembra non essere in grado di far fronte a numero maggiore di 32 bit, ottenere il messaggio FFFFFFFF saturo .


Edit: Aggiornamento - nuovo requisito e la soluzione fornita - non esitate a offrire commenti

Chas. La risposta di Owens è ancora accettata ed eccellente (la parte 2 funziona per me, non ho provato la versione 1 per il Perl più recente, sebbene inviterei altri a confermarlo).

Tuttavia, un altro requisito era quello di essere in grado di convertire indietro dal risultato all'id originale.

Quindi ho scritto il codice per fare questo, ecco la soluzione completa, incluso @Chas. soluzione Owens originale, seguita dalla realizzazione di questo nuovo requisito:

#!/usr/bin/perl 

use strict; 
use warnings; 
use bigint; 

use Carp; 

sub bighex { 
    my $hex = shift; 

    my $part = qr/[0-9a-fA-F]{8}/; 
    croak "$hex is not a 64-bit hex number" 
     unless my ($high, $low) = $hex =~ /^0x($part)($part)$/; 

    return hex("0x$low") + (hex("0x$high") << 32); 
} 

sub to_bighex { 
    my $decimal = shift; 
    croak "$decimal is not an unsigned integer" 
      unless $decimal =~ /^[0-9]+$/; 

    my $high = $decimal >> 32; 
    my $low = $decimal & 0xFFFFFFFF; 

    return sprintf("%08x%08x", $high, $low); 
} 

for my $id (0 ,1, 2, 0xFFFFF, 0x100000, 0x100001, 0x1FFFFF, 0x200000, 0x7FDFFFFF) { 
    my $result = bighex("0x00000200A0000000"); 
    $result += (($id & 0xFFFFF) * 2) + (($id/0x100000) * 0x40000000); 

    my $clusterid = to_bighex($result); 

# the convert back code here: 
my $clusterid_asHex = bighex("0x".$clusterid); 
my $offset = $clusterid_asHex - bighex("0x00000200A0000000"); 
my $index_small_units = ($offset/2) & 0xFFFFF; 
my $index_0x100000_units = ($offset/0x40000000) * 0x100000; 
my $index = $index_0x100000_units + $index_small_units; 


    print "\$id = ".to_bighex($id). 
      " clusterid = ".$clusterid. 
      " back to \$id = ".to_bighex($index). 
      " \n"; 
} 

provare questo codice alla http://ideone.com/IMsp6.

+1

output di 'perl -V: ivsize'? – ysth

+0

Questo non è un messaggio di errore, è un avvertimento. In particolare, uno che il tuo codice potrebbe funzionare dove perl usa interi a 64 bit ma non dove usa interi a 32 bit. Se in effetti hai e avrai sempre interi a 64 bit, disabilitalo con 'no warnings" portable ";' – ysth

+0

@ysth Questa è una pessima idea. Il codice non sarà più portatile allora. Disattivare gli avvisi disattivandoli è una cattiva pratica. Vedi la mia risposta per una soluzione migliore. –

risposta

12
#!/usr/bin/perl 

use strict; 
use warnings; 

use bigint qw/hex/; 

for my $id (0 ,1, 2) { 
    my $result = hex("0x00000200A0000000") + 
     (($id & 0xFFFFF) * 2) + (($id/0x100000) * 0x40000000); 
    printf "%d: %#016x\n", $id, $result; 
} 

Il bigint pragma sostituisce la funzione hex con una versione in grado di gestire numeri che di grandi dimensioni. Inoltre, fa in modo trasparente che gli operatori matematici gestiscano i big inte anziché gli inte sulla piattaforma di destinazione.

Nota, questo funziona solo in Perl 5.10 e versioni successive. Se stai utilizzando una versione precedente di Perl 5, puoi provare questo:

#!/usr/bin/perl 

use strict; 
use warnings; 
use bigint; 

use Carp; 

sub bighex { 
    my $hex = shift; 

    my $part = qr/[0-9a-fA-F]{8}/; 
    croak "$hex is not a 64-bit hex number" 
     unless my ($high, $low) = $hex =~ /^0x($part)($part)$/; 

    return hex("0x$low") + (hex("0x$high") << 32); 
} 

sub to_bighex { 
    my $decimal = shift; 
    croak "$decimal is not an unsigned integer" 
      unless $decimal =~ /^[0-9]+$/; 

    my $high = $decimal >> 32; 
    my $low = $decimal & 0xFFFFFFFF; 

    return sprintf("%08x%08x", $high, $low); 
} 

for my $id (0 ,1, 2) { 
    my $result = bighex("0x00000200A0000000"); 
    $result += (($id & 0xFFFFF) * 2) + (($id/0x100000) * 0x40000000); 
    print "$id ", to_bighex($result), "\n"; 
} 
+1

+1: risposta eccellente ... – dawg

+1

A volte non è necessaria la portabilità. E raggiungere il bigint hammer per fare matematica a 64 bit su un perl a 64 bit non mi piace. Quella chiamata a printf() ti fa già gettare la portabilità fuori dalla finestra. – ysth

+1

@ysth Sì, l'ho visto e ho scritto una seconda versione che dovrebbe funzionare su tutte le macchine a 32 bit. –

Problemi correlati