Visualizzazione dei risultati da 1 a 8 su 8
  1. #1
    Utente di HTML.it
    Registrato dal
    May 2012
    Messaggi
    213

    [c] calcolo epsilon macchina

    Salve, avrei bisogno di un aiuto.
    Dovrei scrivere un programma che mi permetta di calcolare gli epsilon macchina dei float, double e long double e visualizzarne passo dopo passo i bit.
    Credo di esserci riuscito a metà. I float mi funzionano perfettamente, i double non riesco a visualizzarne il binario (mi escono tutti 0, ma l'epsilon è corretto) mentre per i long double ho qualche difficoltà. Vi metto l'esercizio qui, spero che qualcuno di voi possa darmi una mano.
    Sui long double ho veramente scarse conoscenze e non riesco a gestirli...

    codice:
    /* SLIDE 7 - ESERCIZIO 1
                                    TRACCIA
    Scrivere delle function C per calcolare rispettivamente l'epsilon macchina della singola, della doppia precisione e
    della precisione long double, visualizzando ad ogni passo i singoli bit.
    */
    
    
    #include <stdio.h>
    
    void epsmach_sp ();
    void epsmach_dp ();
    void epsmach_ld ();
    void binario_float (long int x);
    void binario_double (long int x);
    
    union ffloat
    {
        float a;
        double aa;
        long double aaa;
        long int la;
        long long int lla;
    }eps, epsp1;
    
    int main()
    {
        printf("Programmazione II - Esercizio 14\n\n");
        printf("EPSILON MACCHINA SINGOLA PRECISIONE\n");
        epsmach_sp();
    
        printf("\n\nESPILON MACCHINA DOPPIA PRECISIONE\n");
        epsmach_dp();
        return 0;
    }
    void epsmach_sp ()
    {
        int i=0;
        eps.a = 1;
        epsp1.a = eps.a+1;
        while (epsp1.a > 1)
        {
            printf("n=%d\teps+1 = %f = ", i,epsp1.a);
            binario_float(epsp1.la);
            eps.a /= 2;
            epsp1.a = eps.a+1;
            i++;
        }
        eps.a = 2.0f*(eps.a);
        printf("\nEpsilon singola precisione = %f", eps.a);
    }
    void epsmach_dp ()
    {
        int i=0;
        eps.aa = 1;
        epsp1.aa = eps.aa+1;
        while (epsp1.aa > 1)
        {
            printf("n=%d\teps+1 = %f = ", i,epsp1.aa);
            binario_double(eps.la);
            eps.aa /= 2;
            epsp1.aa = eps.aa+1;
            i++;
        }
        eps.aa = 2.0*(eps.aa);
        printf("\nEpsilon doppia precisione = %f", eps.aa);
    }
    void epsmach_ld ()
    {
        long double epsp1;
        eps.aaa = 1;
        epsp1 = eps.aaa+1;
        while (epsp1 > 1)
        {
            printf("EPS = %lle = \n", eps.aaa);
            eps.aaa /= 2;
            epsp1 = eps.aaa+1;
        }
        eps.aaa = 2.0*(eps.aaa);
        printf("\nEpsilon long double = %lle", eps.aaa);
    }
    void binario_float (long int x)
    {
        short i;
        for(i=0;i<32;i++)
        {
            if(i == 1 || i == 9)
                printf(" ");
            printf("%ld", (x>>(31-i))&1);
        }
        printf("\n");
    }
    void binario_double (long int x)
    {
        int i;
        for(i=0;i<64;i++)
        {
            if(i == 1 || i == 12)
                printf(" ");
            printf("%ld", (x>>(63-i))&1);
        }
        printf("\n");
    }

  2. #2
    Mi pare che ti complichi la vita inutilmente con quei passaggi a int/long int, specie perché non c'è scritto da nessuna parte che un double abbia le stesse dimensioni di un long (anzi, sulle normali piattaforme x86 a 32 bit ad esempio generalmente non è così, motivo per cui probabilmente ottieni tutti zeri). Inoltre, quell'uso delle union (il cosiddetto "type punning") non è garantito dallo standard (anche se ancora devo vedere un compilatore che non consente quella roba ).

    Il mio consiglio è di lasciare perdere la union e scrivere una singola funzione per scrivere la rappresentazione binaria di un array di char, e usarla per rappresentare i bit dei vari tipi in questa maniera:
    codice:
    double d;
    stampa_binario((unsigned char *)&d, sizeof(d));
    Per quanto riguarda i long double, l'algoritmo per il calcolo dell'epsilon è identico, e per quanto riguarda la stampa dei bit, una volta che hai scritto una funzione stampa_binario il codice è sempre lo stesso.
    (per inciso, su molte piattaforme i long double hanno le medesime dimensioni dei double, per cui se ottieni lo stesso risultato è normale)
    Amaro C++, il gusto pieno dell'undefined behavior.

  3. #3
    Utente di HTML.it
    Registrato dal
    May 2012
    Messaggi
    213
    Ora son di passaggio, però ho letto e compreso in parte il tuo messaggio.
    Per quanto riguarda la union io la uso perchè a me sembra che i caratteri di shift sui float e double non posso utilizzarli...sbaglio?
    Come faccio a calcolarmi il binario da quella tua funzione? Non ho la minima idea...:S

  4. #4
    Originariamente inviato da Eduadie
    Per quanto riguarda la union io la uso perchè a me sembra che i caratteri di shift sui float e double non posso utilizzarli...sbaglio?
    No, infatti. Quello che ti sto dicendo è che:
    • la conversione "a pedate" tramite union tecnicamente non sarebbe consentita;
    • ma soprattutto, dato che a priori non è detto che ci siano interi con le dimensioni di un float o di un double, e se ci sono non è detto che siano sempre gli stessi al variare della piattaforma, lavorare in quel modo con degli interi non è una buona idea.

    Ergo, invece di lavorare con interi fai una conversione ad un array di char, il cui funzionamento è garantito dallo standard e soprattutto non hai problemi di dimensioni diverse al variare della piattaforma - un double è grande esattamente sizeof(double) char, per cui puoi vedere la sua memoria come un array di sizeof(double) char una volta fatto un cast a (unsigned char *) del puntatore alla variabile double in questione (più complicato a dirsi che a farsi, il codice è nel post precedente).

    A questo punto, tutto quello che rimane è scrivere la funzione stampa_binario, che deve stampare la rappresentazione binaria dell'array di unsigned char che gli viene passato.

    Può essere una cosa del tipo:
    codice:
    #include <limits.h>
    
    // ...
    
    void stampa_binario(unsigned char * buffer, unsigned int length)
    {
        for(unsigned int i=0; i<length; i++)
        {
            for(int b=CHAR_BIT-1; b>=0; b--)
                putchar(((buffer[i]>>b)&1)?'1':'0');
            putchar(' ');
        }
    }
    Nota che, su piattaforme little-endian, per vedere la rappresentazione binaria "come te la aspetti" è necessario invertire l'ordine di stampa dei bytes.
    Amaro C++, il gusto pieno dell'undefined behavior.

  5. #5
    Utente di HTML.it
    Registrato dal
    May 2012
    Messaggi
    213
    Scusa per la risposta in ritardo ma devo proprio ringraziarti.
    Son riuscito a risolverlo per quel che mi serviva.
    L'unica cosa è che ho domandato un pò in giro oggi all'università e mi è stato detto che per le macchine a 32 bit non possono rappresentare i long double e mi è stato detto anche che infatti questa parte era facoltativa.
    Ho capito qualcosa sbagliato o è così? Sono comunque curioso.
    Ti ringrazio in anticipo e scusami ancora

  6. #6
    Originariamente inviato da Eduadie
    Scusa per la risposta in ritardo ma devo proprio ringraziarti.
    Son riuscito a risolverlo per quel che mi serviva.
    Ottimo
    L'unica cosa è che ho domandato un pò in giro oggi all'università e mi è stato detto che per le macchine a 32 bit non possono rappresentare i long double e mi è stato detto anche che infatti questa parte era facoltativa.
    Ho capito qualcosa sbagliato o è così? Sono comunque curioso.
    La situazione è un po' più complicata.

    Premessa noiosa:
    • in primis, "macchine a 32 bit" non vuol dire molto - ci sono tante architetture a 32 bit in giro - e in ogni caso, i numeri in floating point non sono normalmente legati alle dimensioni della word nativa del processore, oltre al fatto che le elaborazioni in FP possono essere fatte in software (ad esempio, su un Arduino - che monta un processore AVR a 16 bit - si possono usare tranquillamente dei double a 64 bit, semplicemente tutti i conti sono "emulati" in software).
    • inoltre, per quanto riguarda lo standard C, le dimensioni e la rappresentazione di float, double e long double sono completamente lasciate all'implementazione specifica - e infatti su alcune piattaforme bizzarre (tipicamente microcontrollori o DSP) sono roba diversa da quello che ti stanno insegnando.
    • infine, anche sui PC "normali" (architettura Intel x86/IA32 e estensioni a 64 bit AMD), ci sono più set di istruzioni separati che lavorano con i numeri in virgola mobile (le istruzioni classiche x87 e le varie SSE).


    Ciò detto, normalmente con float e double si intendono le "varianti" binary32 e binary64 dello standard IEEE 754, e così accade su architettura x86 e parenti; lo step successivo è il binary128, ma praticamente nessun processore implementa il supporto hardware per l'aritmetica in tale formato (è raro che serva una precisione del genere, e nel caso si emula in software).

    Piuttosto, il set di istruzioni x87 (originariamente istruzioni per il coprocessore matematico, ormai integrate nei processori di discendenza x86 da tempo immemore) consente di lavorare con numeri in "double extended precision" a 80 bit; per questo motivo alcuni compilatori su architettura x86 usano questi "double estesi" a 80 bit come long double (in particolare, gcc e icc passando un particolare flag).
    D'altra parte, i long double si usano molto di rado (e comunque numeri in virgola mobile ad 80 bit sono una peculiarità delle istruzioni x87, non supportati ad esempio dalle SSE), per cui altri compilatori (ad esempio Visual C++) scelgono di ignorarli completamente: in tal caso long double diventa semplicemente un sinonimo per i normali double (a loro volta i binary64 IEEE 754).
    Amaro C++, il gusto pieno dell'undefined behavior.

  7. #7
    Utente di HTML.it
    Registrato dal
    May 2012
    Messaggi
    213
    Ti ringrazio.
    Anche se qualcosa del tuo discorso è stato molto complicato per me capirlo (argomenti mai affrontati :P ), più o meno son riuscito a capirci qualcosa...
    Ancora grazie.

  8. #8
    Amaro C++, il gusto pieno dell'undefined behavior.

Permessi di invio

  • Non puoi inserire discussioni
  • Non puoi inserire repliche
  • Non puoi inserire allegati
  • Non puoi modificare i tuoi messaggi
  •  
Powered by vBulletin® Version 4.2.1
Copyright © 2024 vBulletin Solutions, Inc. All rights reserved.