Pagina 1 di 2

Calcolo pi greco in C

Inviato: giovedì 11 giugno 2015, 1:31
da Shebang
Salve a tutti, ho voluto pubblicare qui un semplicissimo programma scritto in C che sfrutta una sommatoria molto potente che, con poche iterazioni, da una buonissima espansione decimale di pi greco.
Lo pubblico perchè in rete si trova gran poco di autorevole, il resto sono solo codici scritti male o poco efficienti.
Ma non è l'unico motivo; dato che non ho mai approfondito molto il linguaggio, non saprei come impostare il programma per avere una precisione arbitraria nell'espansione delle cifre decimali, per il limite di memoria delle variabili double.
Vorrei poter printare un numero arbitrario di cifre decimali senza limiti di memoria, come posso aggirare l'ostacolo?
Io pensavo a degli array gestiti da un realloc dove ogni elemento i-esimo è un valore decimale, ma non saprei...

Ecco qui il codice:

Codice: Seleziona tutto

/* Compilare con -lm.
Piccolo programma di esempio sull'uso delle sommatorie, cicli e condizioni per il calcolo ottimizzato di pi greco
*/

#include <math.h>
#include <stdio.h>

int main() {

long long int i;
double k;
double sum;
sum = 0;


printf("\nCoded by Shebang");
printf("\nFirenze 2015\n");


   printf("\nQuesto programma approssima il valore di Pi greco con una rapida espansione decimale grazie ad una sommatoria\nche converge al valore di pi greco molto velocemente denominata Bailey-Borwein-Plouffe, scoperta nel 1995.\n");
   printf("\nInserisci il numero di iterazioni (ì) per approssimare Pi greco con la sommatoria BBP: ");
   scanf("%lld", &i);

   if(i<=0){
   printf("Devi inserire un numero di iterazioni maggiori di 0. \n\n");
      }
   else{

      for(k=0; k<i; k++){
      sum = sum + ((4/(8*k+1))-(2/(8*k+4))-(1/(8*k+5))-(1/(8*k+6)))*(1/(pow(16,k)));
      }

      printf("\nIl valore di Pi greco calcolato per il numero di iterazioni specificato nella sommatoria è:\n %0.100lf \n\n", sum);

      }
return 0;
}

Re: Calcolo pi greco in C

Inviato: giovedì 11 giugno 2015, 2:06
da M_A_W_ 1968
Esistono fin dagli anni Ottanta ottime librerie freeware e con sorgenti dedicate al calcolo in precisione arbitraria: intero, razionale, floating point IEEE 754 e perfino (sebbene assai più raramente) IEEE 854. Finanche negli Snippets di Bob Stout (RIP) a fine anni Ottanta c'erano degli embrionali esempi in merito, assai intuitivi come approccio e prestazionalmente altrettanto limitati.
Una delle più note librerie freeware, presente fin dalle origini in migliaia di BBS, repositories e raccolte di shareware, è senz'altro MIRACL, che solo da pochissimo si è ammantata di questa peculiare connotazione "cripto" così in voga agli inizi del millennio ma che è da sempre nota per prestazioni e affidabilità. Merita senz'altro un cenno anche la libreria MAPM di Michael Ring, che ha goduto della pubblicazione sull'arcinoto CUJ attorno alla svolta del già menzionato millennio. Un lavoro molto più recente e molto ben assestato - non solo nella comunità scientifica (troppo ovvio) ma tra i practitioners - è la MPFR dei ragazzi dell'INRIA.


PS: A prescindere dalle vicissitudini dei fratelli Chudnovski e dell'omonimo algoritmo, Il metodo Bailey-Borwein-Plouffe è comunque molto ben documentato in parecchi testi di calcolo numerico più recenti, oltreché in monografie un po' più specialistiche ma sempre accessibilissime per l'informatico quadratico medio.

Re: Calcolo pi greco in C

Inviato: giovedì 11 giugno 2015, 3:43
da Shebang
Grazie per i riferimenti, penso che analizzerò la libreria MAPM per capire come fare. Per il resto ho dei pdf sul calcolo numerico con la serie sopracitata ma nessuna vera spiegazione su come abbattere il limite della memoria, dovrò cercare meglio.

Re: Calcolo pi greco in C

Inviato: giovedì 11 giugno 2015, 17:35
da Shebang
Sono riuscito ad ottimizzare il codice di 65 byte non implementando la libreria math.h, posto il codice:

Codice: Seleziona tutto

#include <stdio.h>

int main() {

long long int i;
double k;
double sum;
long long int z;
double var;
var = 16;
sum = 0;

printf("\nCoded by Shebang");
printf("\nFirenze 2015");
printf("\n(versione ottimizzata)\n");

    printf("\nQuesto programma approssima il valore di Pi greco con una rapida espansione decimale grazie ad una sommatoria\nche converge al valore di pi greco molto velocemente denominata Bailey-Borwein-Plouffe, scoperta nel 1995.\nIn più non è stata implementata la libreria math.h ottimizzando di 65 byte l'eseguibile\nma rendendo il numero di iterazioni possibili inferiori per le limitazioni di memoria.\n");
    printf("\nInserisci il numero di iterazioni (ì) per approssimare Pi greco con la sommatoria BBP: ");
    scanf("%lld", &i);

    if(i<=0){
    printf("Devi inserire un numero di iterazioni maggiori di 0. \n\n");
        }
    else{

       
/* Adesso un for per inizializzare la sommatoria e salvare il valore di pi greco */

        var = 1; // 16^0
        for(k=0; k<i; k++){
            sum = sum + ((4/(8*k+1))-(2/(8*k+4))-(1/(8*k+5))-(1/(8*k+6)))*(1./var);
            var *= 16; // Calcolo il valore di var da usare nell'iterazione successiva; sostituisce il pow(16, k) che aveva bisogno di math.h
        }

        printf("\nIl valore di Pi greco calcolato per il numero di iterazioni specificato nella sommatoria è:\n %0.100lf \n\n", sum);

        }
return 0;
}

Re: Calcolo pi greco in C

Inviato: giovedì 11 giugno 2015, 22:10
da vbextreme
ti va di trasformarlo in una funzione per l'easyframework? la metterei nella libreria "easymath"

Re: Calcolo pi greco in C

Inviato: venerdì 12 giugno 2015, 0:08
da cortinico
Shebang [url=http://forum.ubuntu-it.org/viewtopic.php?p=4767498#p4767498][img]http://forum.ubuntu-it.org/images/icons/icona-cita.gif[/img][/url] ha scritto:Grazie per i riferimenti, penso che analizzerò la libreria MAPM per capire come fare. Per il resto ho dei pdf sul calcolo numerico con la serie sopracitata ma nessuna vera spiegazione su come abbattere il limite della memoria, dovrò cercare meglio.
Puoi utilizzare un long double. Se non vuoi essere vincolato nemmeno dalla dimensione di una singola variabile devi utilizzare una lista di interi, dove ad ogni elemento associ una cifra della tua approssimazione di Pi. Così facendo riesci a generare molte più cifre...devi però tenere in conto il fatto che dovrai gestirti a mano le operazioni aritmetiche che vuoi fare.

Re: Calcolo pi greco in C

Inviato: venerdì 12 giugno 2015, 14:48
da Shebang
vbextreme [url=http://forum.ubuntu-it.org/viewtopic.php?p=4767834#p4767834][img]http://forum.ubuntu-it.org/images/icons/icona-cita.gif[/img][/url] ha scritto:ti va di trasformarlo in una funzione per l'easyframework? la metterei nella libreria "easymath"
Di che progetto si tratta? Penso che si possa fare, volentieri anche..

Ecco ho letto ora questo post: [Progetto] Easy Framework c v0.4.3 17/05/2015
Leggo che potrebbe essere implementata un giorno nelle distribuzioni, non sarebbe male

Re: Calcolo pi greco in C

Inviato: venerdì 12 giugno 2015, 15:55
da M_A_W_ 1968
Sì ragazzi, finché si tratta di fare esercizio va tutto benissimo, ma quando si parla di sviluppo dei decimali del pigreco c'è inevitabilmente da confrontarsi con dei colossi, a partire dai già menzionati fratelli russi Chudnovsky (i programmi più efficienti al mondo sono basati sul loro algoritmo, e ciò non sorprende), passando da quasi tutte le librerie e i framework di calcolo, fino a estremizzazioni come queste. La formuletta di Bailey-Borwein-Plouffe è elegante e facilmente comprensibile, tanto da poter essere usata in molti corsettini di alfabetizzazione sul calcolo numerico, ma è solamente la punta dell'iceberg, ce ne sono almeno altre tre dozzine di valide elaborate fin dai tempi di Egizi e Sumeri, e parecchie sono anche assai più efficienti computazionalmente rispetto alla BBP.

Parlare addirittura di librerie e distribuzioni in queste condizioni è quantomeno prematuro e poco opportuno. Al massimo, una volta appreso l'ABC del calcolo numerico in multiprecisione, potrai farne uno snippet didattico, meglio se associato ad una libreria come MPFR. Di implementazioni elementari dei vari algoritmi ce ne sono a iosa, sui libri di calcolo numerico e nei repositories.
Questa, per esempio, risale al solito Gauss(*) ed è in giro telematicamente da più di vent'anni, appunto negli snippets del compianto Bob Stout.
L'autore, Carey Bloodworth, ha poi continuato per anni a lavorare sul medesimo tema, ed è anche citato nella pagina linkata sopra sui programmi più veloci per il calcolo dei decimali di pigreco, al numero 9.

Codice: Seleziona tutto

/*
** This method implements the Salamin / Brent / Gauss Arithmetic-
** Geometric Mean pi formula.
**
** Let A[0] = 1, B[0] = 1/Sqrt(2)
**
** Then iterate from 1 to 'n'.
** A[n] = (A[n-1] + B[n-1])/2
** B[n] = Sqrt(A[n-1]*B[n-1])
** C[n]^2 = A[n]^2 - B[n]^2  (or) C[n] = (A[n-1]-B[n-1])/2
**                        n
** PI[n] = 4A[n+1]^2 / (1-(Sum (2^(j+1))*C[j]^2))
**                       j = 1
**
** There is an actual error calculation, but it comes out  to  slightly
** more  than double on each iteration.  I think it results in about 17
** million  correct  digits,  instead  of  16  million  if  it actually
** doubled.  PI16 generates 178,000 digits.  PI19 to  over  a  million.
** PI22 is 10 million, and PI26 to 200 million.
**
** For what little it's worth, this program is placed into the public
** domain by its author, Carey Bloodworth, on September 21, 1996.
**
** The math routines in this program are not general purpose routines.
** They have been optimized and written for this specific use.  The
** concepts are of course portable, but not the implementation.
**
** The program run time is about O(n^1.7).  That's fairly good growth,
** compared to things such as the classic arctangents.  But not as good
** as it should be, if I used a FFT based multiplication.  Also, the 'O'
** is fairly large.  In fact, I'd guess that this program could compute
** one million digits of pi in about the same time as my previously
** posted arctangent method, in spite of this one having less than n^2
** growth.
**
** The program has not been cleaned up.  It's written rather crudely
** and dirty.  The RSqrt() in particular is rather dirty, having gone
** through numerous changes and attempts at speeding it up.
** But I'm not planning on doing any more with it.  The growth isn't as
** low as I'd hoped, and until I find a faster multiplication, the
** method isn't any better than simpler arctangents.
**
** I currently use a base of 10,000 simply because it made debugging
** easier.  A base of 65,536 and modifying the FastMul() to handle sizes
** that aren't powers of two would allow a bit better efficiency.
*/

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include "snipmath.h"

#ifdef __TURBOC__
typedef short int Indexer;
#else
typedef long int Indexer;
#endif

typedef short int Short;
typedef long  int Long;

Short *a, *b, *c, *Work1, *MulWork, *Work2, *Work3;
int SIZE;

/*
** Work1 is explicitly used in Reciprocal() and RSqrt()
** Work1 is implicitly used in Divide() and Sqrt()
** Work2 is explicitly used in Divide() and Sqrt()
** Work3 is used only in the AGM and holds the previous reciprocal
**  square root, allowing us to save some time in RSqrt()
*/


void Copy(Short *a, Short *b, Indexer Len)
{
      while (Len--) a[Len] = b[Len];
}

/*
** This rounds and copies a 2x mul result into a normal result
** Our number format will never have more than one unit of integer,
** and after a mul, we have two, so we need to fix that.
*/

void Round2x(Short *a, Short *b, Indexer Len)
{
      Indexer x;
      Short carry;

      carry = 0;
      if (b[Len+1] >= 5000)
            carry = 1;
      for (x = Len; x > 0; x--)
      {
            carry += b[x];
            a[x-1] = carry % 10000;
            carry /= 10000;
      }
}

void DivBy2(Short *n, Indexer Len)
{
      Indexer x;
      Long temp;

      temp = 0;
      for (x = 0; x < Len; x++)
      {
            temp = (Long)n[x]+temp*10000;
            n[x] = (Short)(temp/2);
            temp = temp%2;
      }
}

void DoCarries(Short *limit, Short *cur, Short carry)
{
      Long temp;

      while ((cur >= limit) && (carry != 0))
      {
            temp = *cur+carry;
            carry = 0;
            if (temp >= 10000)
            {
                  carry = 1;
                  temp -= 10000;
            }
            *cur = temp;
            --cur;
      }
}

void DoBorrows(Short *limit, Short *cur, Short borrow)
{
      Long temp;
      while ((cur >= limit) && (borrow != 0))
      {
            temp = *cur-borrow;
            borrow = 0;
            if (temp < 0)
		{
			borrow = 1;
			temp += 10000;
		}
            *cur = temp;
            --cur;
      };
}

void PrintShort2(char *str, Short *num, Indexer Len)
{
      Indexer x;

      printf("%s ", str);
      printf("%u.", num[0]);
      for (x = 1; x < Len; x++)
            printf("%04u", num[x]);
      printf("\n");
}

void PrintShort(char *str, Short *num, Indexer Len)
{
      Indexer x;
      int printed = 0;

      printf("%s ", str);

      printf("%u.\n", num[0]);

      for (x = 1; x < Len; x++)
      {
            printf("%02d", num[x]/100);
            printed += 2;
            if ((printed % 1000) == 0)
            {
                  printf("\n\n\n\n");
                  printed = 0;
            }
            else if ((printed % 50) == 0)
            {
                  printf("\n");
            }
            else if ((printed % 10) == 0)
            {
                  printf(" ");
            }
            printf("%02d", num[x] % 100);
            printed += 2;
            if ((printed % 1000) == 0)
            {
                  printf("\n\n\n\n");
                  printed = 0;
            }
            else if ((printed % 50) == 0)
            {
                  printf("\n");
            }
            else if ((printed % 10) == 0)
            {
                  printf(" ");
            }
      }
      printf("\n");

}

/* sum = a + b */

Short Add(Short *sum, Short *a, Short *b, Indexer Len)
{
      Long s, carry;
      Indexer x;

      carry = 0;
      for (x = Len - 1; x >= 0; x--)
      {
            s = (Long)a[x] + (Long)b[x] + carry;
            sum[x] = (Short)(s%10000);
            carry = s/10000;
      }
      return carry;
}

/* dif = a-b */

Short Sub(Short *dif, Short *a, Short *b, Indexer Len)
{
      Long d, borrow;
      Indexer x;

      borrow = 0;
      for (x = Len - 1; x >= 0; x--)
      {
            d = (Long)a[x] - (Long)b[x] - borrow;
            borrow = 0;
            if (d < 0)
            {
                  borrow = 1;
                  d += 10000;
            }
            dif[x] = (Short)d;
      }
      return borrow;
}

void Negate(Short *num, Indexer Len)
{
      Indexer x;Long d, borrow;

      borrow = 0;
      for (x = Len - 1; x >= 0; x--)
      {
            d = 0 - num[x] - borrow;
            borrow = 0;
            if (d < 0)
            {
                  borrow = 1;
                  d += 10000;
            }
            num[x] = (Short)d;
      }
}

/* prod = a*b.  prod should be twice normal length */

void Mul(Short *prod, Short *a, Short *b, Indexer Len)
{
      Long p;
      Indexer ia, ib, ip;

      if ((prod == a) || (b == prod))
      {
            printf("MUL product can't be one of the other arguments\n");
            exit(1);
      }

      for (ip = 0; ip < Len * 2; ip++)
            prod[ip] = 0;
      for (ib = Len - 1; ib >= 0; ib--)
      {
            if (b[ib] == 0)
                  continue;
            ip = ib + Len;
            p = 0;
            for (ia = Len - 1; ia >= 0; ia--)
            {
                  p = (Long)a[ia]*(Long)b[ib] + p + prod[ip];
                  prod[ip] = p%10000;
                  p = p/10000;
                  ip--;
            }
            while ((p) && (ip >= 0))
            {
                  p += prod[ip];
                  prod[ip] = p%10000;
                  p = p/10000;
                  ip--;
            }
      }
}

/*
** This is based on the simple O(n^1.585) method, although my
** growth seems to be closer to O(n^1.7)
**
** It's fairly simple.  a*b is: a2b2(B^2+B)+(a2-a1)(b1-b2)B+a1b1(B+1)
**
** For a = 4711 and b = 6397, a2 = 47 a1 = 11 b2 = 63 b1 = 97 Base = 100
**
** If we did that the normal way, we'd do
** a2b2 = 47*63 = 2961
** a2b1 = 47*97 = 4559
** a1b2 = 11*63 =  693
** a1b1 = 11*97 = 1067
**
** 29 61
**    45 59
**     6 93
**       10 67
** -----------
** 30 13 62 67
**
** Or, we'd need N*N multiplications.
**
** With the 'fractal' method, we compute:
** a2b2 = 47*63 = 2961
** (a2-b1)(b1-b2) = (47-11)(97-63) = 36*34 = 1224
** a1b1 = 11*97 = 1067
**
** 29 61
**    29 61
**    12 24
**    10 67
**       10 67
** -----------
** 30 13 62 67
**
** We need only 3 multiplications, plus a few additions.  And of course,
** additions are a lot simpler and faster than multiplications, so we
** end up ahead.
**
** The way it is written requires that the size to be a power of two.
** That's why the program itself requires the size to be a power of two.
** There is no actual requirement in the method, it's just how I did it
** because I would be working close to powers of two anyway, and it was
** easier.
*/

void FastMul(Short *prod, Short *a, Short *b, Indexer Len)
{
      Indexer x, HLen;
      int SignA, SignB;
      Short *offset;
      Short *NextProd;

      /*
      ** This is the threshold where it's faster to do it the ordinary way
      ** On my computer, it's 16.  Yours may be different.
      */

      if (Len <= 16 )
      {
            Mul(prod, a, b, Len);
            return;
      }

      HLen = Len/2;
      NextProd = prod + 2*Len;

      SignA = Sub(prod, a, a + HLen, HLen);
      if (SignA)
      {
            SignA = 1;
            Negate(prod, HLen);
      }
      SignB = Sub(prod + HLen, b + HLen, b, HLen);
      if (SignB)
      {
            SignB = 1;
            Negate(prod + HLen, HLen);
      }

      FastMul(NextProd, prod, prod + HLen, HLen);
      for (x = 0; x < 2 * Len; x++)
            prod[x] = 0;
      offset = prod + HLen;
      if (SignA == SignB)
            DoCarries(prod, offset - 1, Add(offset, offset, NextProd, Len));
      else  DoBorrows(prod, offset - 1, Sub(offset, offset, NextProd, Len));

      FastMul(NextProd, a, b, HLen);
      offset = prod + HLen;
      DoCarries(prod, offset - 1, Add(offset, offset, NextProd, Len));
      Add(prod, prod, NextProd, Len);

      FastMul(NextProd, a + HLen, b + HLen, HLen);
      offset = prod + HLen;
      DoCarries(prod, offset - 1, Add(offset, offset, NextProd, Len));
      offset = prod + Len;
      DoCarries(prod, offset - 1, Add(offset, offset, NextProd, Len));
}

/*
** Compute the reciprocal of 'a'.
** x[k+1] = x[k]*(2-a*x[k])
*/

void Reciprocal(Short *r, Short *n, Indexer Len)
{
      Indexer x, SubLen;
      int iter;
      double t;

      /* Estimate our first reciprocal */

      for (x = 0; x < Len; x++)
            r[x] = 0;
      t = n[0] + n[1]*1.0e-4 + n[2]*1.0e-8;
      if (t == 0.0)
            t = 1;
      t = 1.0/t;
      r[0] = t;
      t = (t - floor(t))*10000.0;
      r[1] = t;
      t = (t - floor(t))*10000.0;
      r[2] = t;

      iter = log(SIZE)/log(2.0) + 1;
      SubLen = 1;
      while (iter--)
      {
            SubLen *= 2;
            if (SubLen > SIZE)
                  SubLen = SIZE;
            FastMul(MulWork, n, r, SubLen);
            Round2x(Work1, MulWork, SubLen);

            MulWork[0] = 2;
            for (x = 1; x < Len; x++)
                  MulWork[x] = 0;
            Sub(Work1, MulWork, Work1, SubLen);

            FastMul(MulWork, r, Work1, SubLen);
            Round2x(r, MulWork, SubLen);
      }
}

/*
** Computes the reciprocal of the square root of 'a'
** y[k+1] = y[k]*(3-a*y[k]^2)/2
**
**
** We can save quite a bit of time by using part of our previous
** results!  Since the number is converging onto a specific value,
** at least part of our previous result will be correct, so we
** can skip a bit of work.
*/

void RSqrt(Short *r, Short *n, Indexer Len, Indexer SubLen)
{
      Indexer x;
      int iter;
      double t;

      /* Estimate our first reciprocal square root */

      if (SubLen < 2 )
      {
            for (x = 0; x < Len; x++)
                  r[x] = 0;
            t = n[0] + n[1]*1.0e-4 + n[2]*1.0e-8;
            if (t == 0.0)
                  t = 1;
            t = 1.0/sqrt(t);
            r[0] = t;
            t = (t - floor(t))*10000.0;
            r[1] = t;
            t = (t - floor(t))*10000.0;
            r[2] = t;
      }

      /*
      ** PrintShort2("\n  ~R: ", r, SIZE);
      */

      if (SubLen > SIZE)
            SubLen = SIZE;
      iter = SubLen;
      while (iter <= SIZE*2)
      {
            FastMul(MulWork, r, r, SubLen);
            Round2x(Work1, MulWork, SubLen);
            FastMul(MulWork, n, Work1, SubLen);
            Round2x(Work1, MulWork, SubLen);

            MulWork[0] = 3;
            for (x = 1; x < SubLen; x++)
                  MulWork[x] = 0;
            Sub(Work1, MulWork, Work1, SubLen);

            FastMul(MulWork, r, Work1, SubLen);
            Round2x(r, MulWork, SubLen);
            DivBy2(r, SubLen);

            /*
            printf("%3d", SubLen);
            PrintShort2("R: ", r, SubLen);
            printf("%3d", SubLen);
            PrintShort2("R: ", r, Len);
            */

            SubLen *= 2;
            if (SubLen > SIZE)
                  SubLen = SIZE;
            iter *= 2;
      }
}

/*
** d = a/b by computing the reciprocal of b and then multiplying
** that by a.  It's faster this way because the normal digit by
** digit method has N^2 growth, and this method will have the same
** growth as our multiplication method.
*/

void Divide(Short *d, Short *a, Short *b, Indexer Len)
{
      Reciprocal(Work2, b, Len);
      FastMul(MulWork, a, Work2, Len);
      Round2x(d, MulWork, Len);
}

/*
** r = sqrt(n) by computing the reciprocal of the square root of
** n and then multiplying that by n
*/

void Sqrt(Short *r, Short *n, Indexer Len, Indexer SubLen)
{
      RSqrt(Work2, n, Len, SubLen);
      FastMul(MulWork, n, Work2, Len);
      Round2x(r, MulWork, Len);
}

void usage(void)
{
      puts("This program requires the number of digits/4 to calculate");
      puts("This number must be a power of two.");
      exit(-1);
}

int main(int argc,char *argv[])
{
      Indexer x;
      Indexer SubLen;
      double Pow2, tempd, carryd;
      int AGMIter;
      int NeededIter;
      clock_t T0, T1, T2, T3;

      if (argc < 2)
            usage();

      SIZE = atoi(argv[1]);
      if (!ispow2(SIZE))
            usage();

      a = (Short *)malloc(sizeof(Short)*SIZE);
      b = (Short *)malloc(sizeof(Short)*SIZE);
      c = (Short *)malloc(sizeof(Short)*SIZE);
      Work1 = (Short *)malloc(sizeof(Short)*SIZE);
      Work2 = (Short *)malloc(sizeof(Short)*SIZE);
      Work3 = (Short *)malloc(sizeof(Short)*SIZE);
      MulWork = (Short *)malloc(sizeof(Short)*SIZE*4);

      if ((a ==NULL) || (b == NULL) || (c == NULL) || (Work1 == NULL) ||
          (Work2 == NULL) || (MulWork == NULL))
      {
            printf("Unable to allocate memory\n");exit(1);
      }

      NeededIter = log(SIZE)/log(2.0) + 1;
      Pow2 = 4.0;
      AGMIter = NeededIter + 1;
      SubLen = 1;

      T0 = clock();

      for (x = 0; x < SIZE; x++)
            a[x] = b[x] = c[x] = Work3[x] = 0;
      a[0] = 1;
      Work3[0] = 2;
      RSqrt(b, Work3, SIZE, 1);
      c[0] = 1;

      T1 = clock();

      /*
      printf("AGMIter %d\n", AGMIter);
      PrintShort2("a AGM: ", a, SIZE);
      PrintShort2("b AGM: ", b, SIZE);
      PrintShort2("C sum: ", c, SIZE);
      */

      while (AGMIter--)
      {
            Sub(Work1, a, b, SIZE);                /* w = (a-b)/2      */
            DivBy2(Work1, SIZE);
            FastMul(MulWork, Work1, Work1, SIZE);  /* m = w*w          */
            Round2x(Work1, MulWork, SIZE);

            carryd = 0.0;                         /* m = m* w^(J+1)   */
            for (x = SIZE - 1; x >= 0; x--)
            {
                  tempd = Pow2*Work1[x] + carryd;
                  carryd = floor(tempd / 10000.0);
                  Work1[x] = tempd - carryd*10000.0;
            }
            Pow2 *= 2.0;
            Sub(c, c, Work1, SIZE);                /* c = c - m        */

            /* Save some time on last iter */

            if (AGMIter != 0)
                  FastMul(MulWork, a, b, SIZE);    /* m = a*b          */
            Add(a, a, b, SIZE);                    /* a = (a+b)/2      */
            DivBy2(a, SIZE);
            if (AGMIter != 0)
            {
                  Round2x(Work3, MulWork, SIZE);
                  Sqrt(b, Work3, SIZE, SubLen); /* b = sqrt(a*b) */
            }
            /*
            printf("AGMIter %d\n", AGMIter);
            PrintShort2("a AGM: ", a, SIZE);
            PrintShort2("b AGM: ", b, SIZE);
            PrintShort2("C sum: ", c, SIZE);
            */
            SubLen *= 2;if (SubLen > SIZE) SubLen = SIZE;
      }

      T2 = clock();

      FastMul(MulWork, a, a, SIZE);
      Round2x(a, MulWork, SIZE);
      carryd = 0.0;
      for (x = SIZE - 1; x >= 0; x--)
      {
            tempd = 4.0*a[x] + carryd;
            carryd = floor(tempd / 10000.0);
            a[x] = tempd - carryd*10000.0;
      }

      Divide(b, a, c, SIZE);
      T3 = clock();

      PrintShort("\nPI = ", b, SIZE);

      printf("\nTotal Execution time: %12.4lf\n",
		 (double)(T3 - T0) / CLOCKS_PER_SEC);
      printf("Setup time          : %12.4lf\n",
		 (double)(T1 - T0) / CLOCKS_PER_SEC);
      printf("AGM Calculation time: %12.4lf\n",
		 (double)(T2 - T1) / CLOCKS_PER_SEC);
      printf("Post calc time      : %12.4lf\n",
		 (double)(T3 - T2) / CLOCKS_PER_SEC);

      return 0;
} 
PS: Su molti compilatori, long double e double coincidono. Gli standard si limitano a sancire una relazione d'ordine debole tra i due tipi.



(*) E pensare che una volta, su un noto forum di elettronica, un vecchio fisico applicativo faceva notare con tono accorato che perfino l'algoritmo di Cooley-Tuckey per la FFT era in qualche misura stato anticipato da Gauss in una sua procedura per il calcolo di chissà quale effemeride, e dovrebbe quindi portarne il nome. Aggiungendo poi, con nostro sommo sghignazzamento, che "per fortuna Gauss ha avuto comunque la gloria che meritava". Al che io feci notare di rimando che, scelto arbitrariamente un testo di matematica dallo scaffale, se anche cancellassimo sistematicamente la metà dei riferimenti al nome di Gauss, restrebbe comunque il più citato in assoluto. Impossibile non trovarselo tra i piedi, anche in settori della matematica nati secoli dopo la sua morte... :lol:

Re: Calcolo pi greco in C

Inviato: venerdì 12 giugno 2015, 17:16
da Shebang
Questo poco ma sicuro; l'algoritmo di Gauss-Legendre dopo 20 iterazioni ha più cifre esatte che memoria disponibile ahahah.
Per quanto riguarda la BBP... esistono suoi miglioramenti come la Bellard, di sicuro non pretendo che questa scemenza di codice venga usato per la ricerca :lol: se poi persone che si scrivono le proprie librerie mi chiedono di implementarlo a me può far solo piacere :birra:
Per quanto riguarda il problema sto solo sperimentando dandomi risposte via via sempre più precise su come poter fare da piccolo studente di fisica, consapevole che sono stati già ""risolti"" più elegantemente ed efficientemente (magari direttamente in assembly, di cui ho una conoscenza limitata).

Re: Calcolo pi greco in C

Inviato: venerdì 12 giugno 2015, 17:32
da M_A_W_ 1968
Shebang [url=http://forum.ubuntu-it.org/viewtopic.php?p=4768123#p4768123][img]http://forum.ubuntu-it.org/images/icons/icona-cita.gif[/img][/url] ha scritto:Per quanto riguarda il problema sto solo sperimentando dandomi risposte via via sempre più precise su come poter fare da piccolo studente di fisica, consapevole che sono stati già ""risolti"" più elegantemente ed efficientemente (magari direttamente in assembly, di cui ho una conoscenza limitata).
Lo sviluppo in Assembly sulle macchine mainstream odierne richiede un lungo apprendistato e rischia comunque di dover cedere il passo a "scorciatoie" hardware come il ricorso alla programmazione GPGPU.

In ogni caso, il dilemma ha due corni: un fisico applicativo risolve la maggior parte dei suoi problemi negli ambienti di calcolo e con l'uso di Python, come può peraltro confermare anche il mio vecchio amico VaeVictis.
Ma apprendere almeno le fondamenta del calcolo numerico "hardcore" implementato in C, C++ e magari Fortran ha un valore didattico inestimabile e va incoraggiato con ogni mezzo, nonostante le sempre più ampie carenze didattiche degli ultimi anni. Per questo secondo aspetto esorto tuttavia ad uno studio sistematico sulle vere e proprie Bibbie del calcolo numerico, più e più volte citate in passato. Gli aspetti applicativi richiesti dall'uso di linguaggi di livello intermedio (C) e basso (Assembly) sono comunque prevalenti rispetto alle nozioni di computazione discreta nei domini del continuo e richiedono, anche qui, un apprendistato notevole per addivenire a risultati mediamente efficienti o meramente usabili.

Re: Calcolo pi greco in C

Inviato: venerdì 12 giugno 2015, 19:02
da Shebang
M_A_W_ 1968 [url=http://forum.ubuntu-it.org/viewtopic.php?p=4768132#p4768132][img]http://forum.ubuntu-it.org/images/icons/icona-cita.gif[/img][/url] ha scritto:
Shebang [url=http://forum.ubuntu-it.org/viewtopic.php?p=4768123#p4768123][img]http://forum.ubuntu-it.org/images/icons/icona-cita.gif[/img][/url] ha scritto:Per quanto riguarda il problema sto solo sperimentando dandomi risposte via via sempre più precise su come poter fare da piccolo studente di fisica, consapevole che sono stati già ""risolti"" più elegantemente ed efficientemente (magari direttamente in assembly, di cui ho una conoscenza limitata).
Lo sviluppo in Assembly sulle macchine mainstream odierne richiede un lungo apprendistato e rischia comunque di dover cedere il passo a "scorciatoie" hardware come il ricorso alla programmazione GPGPU.
Perchè costituisce un rischio la ""scorciatoia"" hardware?

Re: Calcolo pi greco in C

Inviato: venerdì 12 giugno 2015, 19:21
da vbextreme
L'easy framework è una semplice raccolta di funzioni utili a chi sta studiando il linguaggio c, non mira ad essere distribuito in qualche esotica distro, cerca invece di aiutare gli studenti.
Se hai voglia di contribuire sei il benvenuto, è anche un buon metodo per imparare e per non disperdere nel nulla il proprio codice.

Gauss, già io ho faticato ad implementare mth_randomgauss() una funzione che ritorna un numero pseudocasuale secondo la curva di gaus, ottimo per creare dei giochi, naturalmente la trovate nella libreria easymath.

il resto per me è arabo.

Re: Calcolo pi greco in C

Inviato: venerdì 12 giugno 2015, 19:51
da Shebang
vbextreme [url=http://forum.ubuntu-it.org/viewtopic.php?p=4768185#p4768185][img]http://forum.ubuntu-it.org/images/icons/icona-cita.gif[/img][/url] ha scritto:L'easy framework è una semplice raccolta di funzioni utili a chi sta studiando il linguaggio c, non mira ad essere distribuito in qualche esotica distro, cerca invece di aiutare gli studenti.
Se hai voglia di contribuire sei il benvenuto, è anche un buon metodo per imparare e per non disperdere nel nulla il proprio codice.

Gauss, già io ho faticato ad implementare mth_randomgauss() una funzione che ritorna un numero pseudocasuale secondo la curva di gaus, ottimo per creare dei giochi, naturalmente la trovate nella libreria easymath.

il resto per me è arabo.
Si sono d'accordo, puoi prenderlo quel codice. Per le distribuzioni quando ho letto le regole della sezione devo aver frainteso qualcosa.
Ma comunque sia puoi inserirlo.

Re: Calcolo pi greco in C

Inviato: venerdì 12 giugno 2015, 20:32
da vbextreme
Veramente io non ho la piu pallida idea di cosa faccia quel programma ed è per quello che ti ho chiesto se volevi trasformarlo in una funzione da aggiungere all'easyframework.

Re: Calcolo pi greco in C

Inviato: venerdì 12 giugno 2015, 20:54
da gila75
Interessante questa cosa, non la conoscevo.
Qui ho trovato la documentazione, che però non ho approfondito
Ho provato il codice e più iterazioni fai, più ti avvicini al PI reale.
la variabile z però è inutilizzata.

EDIT: dopo un po' di cifre però (indipendentemente dalle iterazioni, nel mio caso 10000), si discosta dal valore reale di pi.
Credo sia dovuta al fatto che un double dopo un po' raggiunge il suo limite
10000 iterazioni:

Codice: Seleziona tutto

 3.1415926535897931159979634685441851615905761718750000000000000000000000000000000000000000000000000000 
reale:

Codice: Seleziona tutto

3,14159 26535 89793 23846 26433 83279 50288 41971 69399 37510 58209 74944 59230 78164 06286 20899 86280 34825 34211 70679…

Re: Calcolo pi greco in C

Inviato: venerdì 12 giugno 2015, 22:29
da M_A_W_ 1968
Sfortunatamente i troppi pastrocchi appiccicati alle CPU mainstream per garantire la retrocompatibilità (i.e. microcodice) e cercare di recuperare prestazioni nonostante i limiti di integrazione suggeriti dalla curva empirica di Moore (es. cache multiple, threading, pipelining profondo...) rendono arduo ottenere il massimo throughput, in un panorama eccessivamente fluido. Da qui l'orientamento "moderno", ben esposto da personaggi ben in vista nella comunità internazionale come Randall Hyde o Agner Fog, di ricorrerre sistematicamente ai builtins dei compilatori C/C++ più evoluti, limitando drasticamente il ricorso all'Assembly, anche inline. Si rischia infatti, molto concretamente, di non avere le risorse necessaria alla progettazione, stesura, profiling accurato di ampie sezioni di codice Assembly, le cui prestazioni peraltro possono variare drasticamente passando ad un modello di CPU affine: il che implica la manutenzione di numerose versioni differenti, gestite in parallelo, una per ogni CPU Intel e AMD ragionevolmente presente nelle macchine target. Un impegno di engineering non indifferente.

In questo filone si inserisce l'idea del general purpose GPU computing, il cui maggior limite è però costituito di nuovo dalla necessità di legarsi ad uno specifico componente hardware, una (o più) scheda video, appoggiandosi a librerie proprietarie. Dunque si tratta in realtà di un rischio molto concreto, per la maggior parte dei progetti commerciali che non dispongono di risorse sufficienti a gestire l'ampia varietà di casi presenti contemporaneamente (ed in continua evoluzione) sul mercato mainstream.

Questo non significa certo che la programmazione in Assembly sia un rottame da abbandonare, ma piuttosto un'arte esoterica da coltivare, e anche piuttosto costosa se calata in un contesto di engineering altamente organizzato.

Re: Calcolo pi greco in C

Inviato: sabato 13 giugno 2015, 18:43
da Shebang
Per le mie esperienze con l'assembly (programmi interamente scritti in fasm) ho sempre avuto codici molto più piccoli dei corrispettivi scritti in C ed infinitamente più veloci (grazie molte volte proprio ai comandi eseguiti direttamente dall'hardware dei processori) scoprendo delle volte che le librerie del C non sono ottimizzate per niente, le quali svolgono compiti che in assembly possono essere riottimizzati con altre istruzioni (tipo nelle librerie c'è ogni tanto un mov edi edi solo per saltare dei cicli di clock) . Ora non stiamo a discutere sull'utilità di scrivere programmi interamente scritti in asm, ma penso che un programma delle dimensioni, velocità ed ottimizzazione dell'assembly, adattandolo alle componenti hardware dei supercomputer che hanno a disposizione per la ricerca, sia l'unica soluzione, quello intendevo. Ma questa è la mia umile opinione a riguardo, non sò se sia cosi semplice.

Re: Calcolo pi greco in C

Inviato: sabato 13 giugno 2015, 19:41
da M_A_W_ 1968
Sfortunatamente le cose non sono così lineari, specialmente nel mainstream. E qui faccio appello al principio di autorità :lol:, perché (come già spiegato nella notte dei tempi) il mio nickname è l'acronimo di Master Assembly Wizard, programmo in Assembly su almeno una sessantina di famiglie principali di processori e microcontroller da circa sette lustri, da decenni combatto sulle barricate per riaffermare che nulla può battere in efficienza e/o compattezza l'implementazione Assembly accuratamente ottimizzata di un dato algoritmo... e se oggi perfino io arrivo ad ammettere che il ricorso all'Assembly nel mainstream è una decisione ingegneristicamente assai più rara e sofferta che in passato, lo faccio davvero obtorto collo e con le lacrime agli occhi. :cry:

Gli Agner Fog e i Randy Hyde che oggi confermano la via del ricorso ai builtins sui processori mainstream sono comunque nuovi profeti rispetto a quanto dicevamo in passato nella comunità dell'ottimizzazione del codice, nella quale i personaggi di spicco si chiamavano Carmack, Booth, Abrash. La tradizione dell'Assembly e dei suoi cultori (e guru) ha comunque un filone ininterrotto, che si concretizza sulle migliaia di piattaforme embedded e in generale nel più vasto mondo dei sistemi dedicati, ma con dei distinguo.
In breve, anche l'ambito del supercalcolo oggi è assai diverso rispetto al passato: prevalgono infatti cluster di semplici PC rispetto ad altre architetture, specialmente quelle a parallelismo massivo, ma anche nel mondo dei mainframe tradizionali (es. IBM Z9, Z10) si è ormai da tempo orientati sulla linea di una progettazione delle ISA (instruction set dei processori) che rendano assai più la facile la produzione automatica del codice ottimizzato. D'altro canto ormai perfino nei microcontroller RISC di fascia mediobassa si tende ad inserire un gruppo di istruzioni C-friendly, anche se in tali ambiti l'ottimizzazione ottenibile con i migliori cross compiler è ancora ben lungi da quella possibile per un programmatore allenato.

La questione delle librerie standard e di runtime del C è ancora diversa: vi sono compilatori più inclini ad ottenere la massima portabilità (vedi GCC) e altri più legati ad ambiti specifici, nei quali maggiore attenzione è posta alle ottimizzazioni verticali platform-oriented. Che occorra poco per mettere in crisi gli ottimizzatori moderni dai quali tanto si favoleggia è comunque un dato di fatto, e vi sono miriadi di esempi pratici al riguardo (anche sul blog del sottoscritto :lol: ).

In ogni caso, come si ripete da sempre, solo analisi del codice prodotto, misure puntuali e profiling hanno l'ultima parola in ordine alla decisione progettuale e implementativa di ricorrere all'Assembly per ottimizzare manualmente sezioni "core" di codice, di ampiezza ragionevole. Il fatto che oggi l'ottimizzazione manuale sulle CPU x86 e IA64 sia più difficile non significa certo la morte dell'Assembly (jamais!), ma semplicemente cambia i costi e conseguentemente gli orientamenti nelle decisioni di progetto. L'Assembly è vivo e vegeto, specialmente fuori dal mondo mainstream! :p

Re: Calcolo pi greco in C

Inviato: lunedì 15 giugno 2015, 22:17
da Shebang
vbextreme [url=http://forum.ubuntu-it.org/viewtopic.php?p=4768221#p4768221][img]http://forum.ubuntu-it.org/images/icons/icona-cita.gif[/img][/url] ha scritto:Veramente io non ho la piu pallida idea di cosa faccia quel programma ed è per quello che ti ho chiesto se volevi trasformarlo in una funzione da aggiungere all'easyframework.
Ok la scrivo, ti chiederò informazioni via mp.

Re: Calcolo pi greco in C

Inviato: martedì 16 giugno 2015, 0:26
da Shebang

Codice: Seleziona tutto

#include <stdio.h>
#include <math.h>

void BBPpigreco(long long int i,double sum){
double k;
sum = 0;

printf("\nCoded by Shebang\nFirenze 2015\n");
printf("\nQuesta funzione approssima il valore di Pi greco con una rapida espansione decimale grazie ad una sommatoria\nche converge al valore di pi greco molto velocemente denominata Bailey-Borwein-Plouffe, scoperta nel 1995.\n");
printf("\nInserisci il numero di iterazioni (ì) per approssimare Pi greco con la sommatoria BBP: ");
scanf("%lld", &i);

	if(i<=0){
	printf("Devi inserire un numero di iterazioni maggiori di 0. \n\n");
	}
	else{

	for(k=0; k<i; k++){
	sum = sum + ((4/(8*k+1))-(2/(8*k+4))-(1/(8*k+5))-(1/(8*k+6)))*(1/(pow(16,k)));
	}

	printf("\nIl valore di Pi greco calcolato per il numero di iterazioni specificato nella sommatoria è:\n %0.100lf \n\nEseguito.\n\n", sum);
	}
return;
}


// Prova la funzione BBPpigreco; togli il main() dai commenti per provare il programma con la funzione BBPpigreco()

/*
int main(){

long long int i;
double sum;

BBPpigreco(i,sum);

return 0;
}
*/
La funzione si chiama BBPpigreco(i,sum);

A me funge togliendo la funzione main() dai commenti.
Migliorie? Ora non sono stato a guardare la libreria easymath, quando avrò un attimo.