Operazioni con precisione quadrupla a 128-bit con numeri in virgola mobile mediante la libreria quadmath.h

Da Gambas-it.org - Wikipedia.

Come è noto, in Gambas nelle operazioni con numeri in virgola mobile il tipo di valore che consente la più alta precisione è il Float (che nel C corrisponde al tipo chiamato double).

Questo tipo di valore nativo nei sistemi a 64-bit occupa 8 byte (64-bit) e rappresenta un valore in virgola mobile con precisione doppia. La precisione del Float è di 52 cifre binarie che sono 16 cifre decimali (2^-52).

Precisioni superiori non sono possibili in Gambas con i tipi di valore nativi.


Superare i limiti di Gambas della doppia precisione del Float con i numeri in virgola mobile mediante la libreria "quadmath.h"

Possiamo ovviare al limite di Gambas della doppia precisione del Float con i numeri in virgola mobile, utilizzando la libreria "quadmath.h", la quale fornisce una precisione quadrupla a 128-bit (16 byte).

Potremo utilizzare in Gambas le risorse e le potenzialità di detta libreria, realizzando un'apposita libreria dinamica condivisa esterna .so.

Pur fornendo la libreria quadmath.h proprie funzioni matematiche e trigonometriche, nell'esempio che faremo di seguito ci limiteremo a creare una libreria condivisa che consenta di effettuare le quattro principali operazioni (addizione, sottrazione, moltiplicazione e divisione) fra due numeri in virgola mobile.

La libreria condivisa, da noi creata, possiede un'unica funzione, così dichiarata:

char * Float128q(int dec, char *num1, char *num2, char *opera)

e contenente le righe di codice per effettuare, a seconda dell'identificativo passato, una delle quattro operazioni predette.
Riguardo ai suoi parametri:
- il primo è un intero che rappresenta il numero di decimali dopo la virgola che saranno mostrati;
- il secondo è un Puntatore ad un vettore di valori di tipo carattere che rappresenta il primo numero oggetto dell'operazione;
- il terzo è un Puntatore ad un vettore di valori di tipo carattere che rappresenta il secondo numero oggetto dell'operazione;
- il quarto è un Puntatore ad un vettore di valori di tipo carattere che rappresenta l'identificatore dell'operazione prescelta da far compiere.

La funzione restituisce un Puntatore ad un vettore di valori di tipo carattere che rappresenta il risultato dell'operazione da noi scelta.


Tutto ciò in Gambas sarà così dichiarato con Extern:

Private Extern Float128q(dcm As Integer, num1 As String, num2 As String, opera As String) As String

Bisogna ricordare che per numeri in virgola mobile, passati nel secondo e terzo argomento della funzione esterna Float128q( ), il segno separatore della parte intera dalla parte decimale dovrà essere quello locale, ossia da noi la virgola.


Mostriamo dunque un esempio pratico, nel quale scegliamo di far effettuare una divisione fra i due numeri passati, e di ottenere un risultato avente la parte decimale formata da 32 numeri:

Library "/tmp/libfloat128q"

' char * Float128q(int dcm, char *num1, char *num2, char *opera)
' La funzione contenuta nella nostra libreria dinamica per la gestione delle risorse della libreria esterna "quadmath.h"
Private Extern Float128q(dcm As Integer, num1 As String, num2 As String, opera As String) As String


Public Sub Main()
 
 Dim operazione As String[] = ["add", "sot", "mol", "div"]  ' Imposta gli identificatori delle 4 operazioni principali: "addizione", "sottrazione", "moltiplicazione" e "divisione"
 Dim ver, s As String
 
' Cerca la cartella della versione di gcc contenente il file header "quadmath.h":
  ver = Versione()
 
' Va a creare la nostra apposita libreria dinamica esterna .so per la gestione delle risorse della libreria esterna "quadmath.h":
  CrealibFloat128q(ver)  
 
' Per i numeri in virgola mobile si deve usare la "virgola" separatrice della parte decimale da quella intera:
  s = Float128q(32, "5,123456789123456789", "3,123456789", operazione[3])
 
  Print "Risultato: "; s
   
End


Private Function Versione() As String
 
 Dim dd As String[]
 Dim percorso, d As String
  
  percorso = "/usr/lib/gcc/x86_64-linux-gnu/"
  
  dd = Dir(percorso, Null, gb.Directory).Sort(gb.Descent)
   
  For Each d In dd
    If Exist(percorso &/ d &/ "quadmath.h") Then Exit
  Next
  
  Return d
  
End


Private Procedure CrealibFloat128q(v As String)
 
' Scrive il codice sorgente della nostra apposita libreria dinamica esterna:
  File.Save("/tmp/libfloat128q.c", "#include </usr/lib/gcc/x86_64-linux-gnu" &/ v &/ "include/quadmath.h>\n" &
                                   "#include <stdio.h>\n" &
                                   "#include <string.h>" &
                                   "\n\n" &
                                   "char buf[128];" &
                                   "\n\n\n" &
                                   "char * Float128q(int dcm, char *num1, char *num2, char *opera) {" &
                                   "\n\n" &
                                   "   char str[4];\n" &
                                   "   char dest[16] = \"%+-#*.\";\n" &
                                   "   __float128 fl1, fl2, ris;" &
                                   "\n\n\n" &
                                   "   sprintf(str, \"%d\", dcm);\n" &
                                   "   strcat(dest, str);\n" &
                                   "   strcat(dest, \"Qe\");" &
                                   "\n\n" &
                                   "   fl1 = strtoflt128(num1, NULL);\n" &
                                   "   fl2 = strtoflt128(num2, NULL);" &
                                   "\n\n" &
                                   "   if (strcmp(opera, \"add\") == 0 ) {\n" &
                                   "         ris = fl1 + fl2;\n   }" &
                                   "\n\n" &
                                   "   if (strcmp(opera, \"sot\") == 0 ) {\n" &
                                   "         ris = fl1 - fl2;\n   }" &
                                   "\n\n" &
                                   "   if (strcmp(opera, \"mol\") == 0 ) {\n" &
                                   "         ris = fl1 * fl2;\n   }" &
                                   "\n\n" &
                                   "   if (strcmp(opera, \"div\") == 0 ) {\n" &
                                   "         ris = fl1 / fl2;\n   }" &
                                   "\n\n\n" &
                                   "   quadmath_snprintf(buf, 128, dest, ris);
                                   "\n\n" &
                                   "   return buf;\n\n}")
  
' Genera la nostra apposita libreria dinamica esterna:
  Shell "gcc -o /tmp/libfloat128q.so /tmp/libfloat128q.c -shared -fPIC -lquadmath" Wait
  
End



Riferimenti