Gestire numeri superiori al limite massimo supportato dal tipo Long

Da Gambas-it.org - Wikipedia.

Come è noto, in Gambas il tipo di variabile Long in Gambas è capace di contenere valori positivi non superiori al numero 9.223.372.036.854.775.807. [Nota 1]

Per poter ottenere dalla somma (o dalla moltiplicazione) di due o più numeri un risultato superiore al limite massimo rappresentabile in Gambas dal valore Long, si potranno adottare alcune modalità [Nota 2][Nota 3] che di seguito descriveremo.

Uso della Classe Classe "BigInt" del Componente gb.gmp

Per poter superare i limiti del suo nativo tipo Long, Gambas fornisce un apposito Componente, denominato "gb.gmp". In particolare questo Componente è costituito da due Classi: "BigInt", che rappresenta un numero intero con numero potenziale illimitato di cifre, e "Rational", dedicato alla gestione dei numeri razionali.

Mostriamo un semplice esempio pratico di utilizzo della Classe BigInt con le quattro operazioni basilari:

Public Sub Main()
 
 Dim s1, s2 As String
 Dim addi, sott, molt, divi As BigInt
 
  s1 = "9999999999999999999999999999999999999999999999999999999999999999999999999999"
  s2 = "8888888888888888888888888888888888888888888888888888888888888888888888888888"
  
  addi = BigInt.FromString(s1) + BigInt.FromString(s2)
  sott = BigInt.FromString(s1) - BigInt.FromString(s2)
  molt = BigInt.FromString(s1) * BigInt.FromString(s2)
  divi = BigInt.FromString(s1) / BigInt.FromString(s2)
  
  Print addi
  Print sott
  Print molt
  Print divi
  
End


Uso della funzione esterna sprintf( )

Questa modalità prevede la conversione della somma di due Long di Gambas nel formato del tipo unsigned long long [Nota 4], e, affinché possa essere rappresentato dalle risorse di Gambas, si effettuerà la conversione del risultato in una stringa (ma comunque visivamente rappresentato in forma decimale) mediante la funzione esterna sprintf( ) di C.

Il risultato della somma dei due valori non potrà comunque superare il valore massimo rappresentabile del predetto tipo unsigned long long del linguaggio C.


Mostriamo un esempio:

' int sprintf(char *str, const char *format, ...)
' Sends formatted output to a string pointed to, by str.
Private Extern sprintf(strP As Pointer, formatS As String, lo As Long) As Integer In "libc:6"


Public Sub Main()

 Dim l As Long
 Dim p As Pointer
 
' Assegnamo alla variabile di tipo "Long" il valore massimo assegnabile per un tipo "Long" in Gambas:
  l = 9223372036854775807

  p = Alloc(24)

' Moltiplichiamo il valore della variabile per 2. Convertiamo il risultato nel tipo 'unsigned long long' del C.
' Al termine convertiamo il risultato in una stringa che sarà posta mediante la funzione 'sprintf()' nell'area di memoria puntata dal 'Puntatore':
  sprintf(p, "%llu", l * 2)
  If String@(p) = "0" Then Error.Raise("Errore: è stato superato il valore massimo (18446744073709551615ULL) del tipo 'unsigned long long' del C !")
  
  Print "Valore massimo rappresentabile dal 'Long': 9223372036854775807\n"

  Print "Risultato superiore al 'Long' ottenuto:   "; String@(p)
   
' Liberiamo la memoria precedentemente riservata e ci assicuriamo che il Puntatore non punti ad alcuna cella utile:
  Free(p)
  p = 0

End


Caso in cui il numero è espresso in rappresentazione "binaria"

Qualora il valore numerico, superiore al limite massimo rappresentabile in Gambas dal valore Long, sia espresso in rappresentazione binaria, si potrà adottare un'apposita funzione esterna, da noi scritta in puro C, con la quale si farà uso della funzione esterna strtoul( ), dichiarata nel file header "/usr/include/stdlib.h". Tale funzione esterna strtoul( ) converte un numero, scritto in formato stringa, in un valore numerico di tipo unsigned long int del linguaggio C [Nota 5], qualunque sia la base, la quale sarà specificata nel suo terzo parametro.
Il valore binario infine sarà convertito mediante la funzione esterna "sprintf( )" in una rappresentazione di tipo stringa, ma visivamente in forma decimale.

Mostriamo un esempio pratico:

Library "/tmp/lib"

' char * bindecstring(const char * binario)
' Converte un numero binario in numero in rappresentazione decimale e di tipo string.
Private Extern bindecstring(binario As String) As String


Public Sub Main()
 
 Dim bnr, rit As String
 
  CreaSo()
  
' Il valore espresso in rappresentazione "binaria" da mostrare in forma decimale, ma di tipo stringa:
  bnr = "1111111111111111111111111111111111111111111111111111111111111111"
  
  rit = bindecstring(bnr)
  
  Print rit
     
End


Private Procedure CreaSo()
 
 File.Save("/tmp/lib.c", "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n" &
           "char * bindecstring(const char * binario) {\n\n" &
           "   unsigned long int ris;\n"
           "   char *rit = calloc(72, sizeof(char));\n\n" &
           "   ris = strtoul(binario, NULL, 2);\n" &
           "   sprintf(rit, \"%lu\", ris);\n\n" &
           "   return rit;\n\n}")
 
 Shell "gcc -o /tmp/lib.so /tmp/lib.c -shared -fPIC" Wait
 
End



Note

[1] Come è noto, il tipo nativo Long di Gambas corrisponde sostanzialmente al tipo "signed long int" del linguaggio C (vedi anche il file header "/usr/include/limits.h").

[2] Vedere anche: Operazioni con precisione quadrupla a 128-bit con numeri in virgola mobile mediante la libreria quadmath.h

[3] Interessante vedere anche la libreria esterna "BigNumber", autori Davide Zanin e Frhack3r.

[4] Il tipo unsigned long long è specificato nel file header "/usr/include/limits.h".

[5] Il tipo unsigned long int è specificato nel file header "/usr/include/limits.h".