Torna indietro   Hardware Upgrade Forum > Software > Programmazione

Sony WF-1000X M6: le cuffie in-ear di riferimento migliorano ancora
Sony WF-1000X M6: le cuffie in-ear di riferimento migliorano ancora
WF-1000X M6 è la sesta generazione di auricolare in-ear sviluppata da Sony, un prodotto che punta a coniugare facilità di utilizzo con una elevata qualità di riproduzione dei contenuti audio e una cura nella riduzione del rumore ambientale che sia da riferimento
Snowflake porta l'IA dove sono i dati, anche grazie a un accordo con OpenAI
Snowflake porta l'IA dove sono i dati, anche grazie a un accordo con OpenAI
Snowflake ha presentato diverse novità per la sua piattaforma legate all'intelligenza artificiale. Quella forse più eclatante è una collaborazione con OpenAI, ma non mancano diverse nuove funzionalità che rendono la piattaforma più flessibile e in grado di rispondere meglio alle esigenze in continuo cambiamento delle aziende
Sistema Mesh Roamii BE Pro: il Wi-Fi 7 secondo MSI
Sistema Mesh Roamii BE Pro: il Wi-Fi 7 secondo MSI
Con velocità teoriche fino a 11 Gbps, gestione tramite app intelligente e protezione avanzata dei dispositivi, Roamii BE Pro porta il Wi‑Fi 7 tri‑band nelle abitazioni più esigenti. Un sistema Wi-Fi Mesh proposto da MSI allo scopo di garantire agli utenti una rete fluida e continua capace di sostenere streaming 8K, gaming competitivo e le applicazioni moderne più esigenti in termini di banda
Tutti gli articoli Tutte le news

Vai al Forum
Rispondi
 
Strumenti
Old 17-10-2011, 16:33   #1
Zero-Giulio
Member
 
Iscritto dal: May 2007
Messaggi: 292
[C++] Meccaniche segrete del compilatore

Ok, premetto che non sono un grande esperto di programmazione.
Magari quello che mi succede è normalissimo e nessuno di voi si stupirebbe. Ma a me sembra inspiegabile e quindi vi chiedo lumi.

Guardate un attimo queste 5 righe di codice:

Codice:
double value = 0.0;
value += (ptr->_pdd * ptr->_ndd->_temp3);
value += (ptr->_pdu * ptr->_ndu->_temp3);
value += (ptr->_pud * ptr->_nud->_temp3);
value += (ptr->_puu * ptr->_nuu->_temp3);
Niente di che. La variabile double value viene inizializzata a zero, e successivamente incrementata 4 volte.

Ora guardate queste 3 righe di codice:

Codice:
double temp = 0.0;
double ttt = (ptr->_pdd * ptr->_ndd->_temp3)
              + (ptr->_pdu * ptr->_ndu->_temp3)
              + (ptr->_pud * ptr->_nud->_temp3)
              + (ptr->_puu * ptr->_nuu->_temp3);
temp = ttt;
Anche qui, nulla di strano. Nella variabile double ttt sommo 4 addendi, e poi a temp assegno ttt.

Notate che i 4 addendi nei due estratti di codice sono gli stessi.

Io sarei portato a pensare che value e temp assumano lo stesso valore. E invece NO!!!

Qualcuno sa spiegarmi perchè?

C'è qualcosa che mi sfugge, ma non so cosa. I miei colleghi non riescono ad aiutarmi.

Ovviamente posso darvi tutte le informazioni che volete sul codice in questione, anche se non credo siano molto rilevanti.
Per la cronaca, ptr è un puntatore a un oggetto, il quale oggetto ha, tra gli altri attributi, il double temp3, 4 double (pdd, pdu, pud, puu) e 4 puntatori a oggetti come lui.
Quindi le variabili temp, ttt, value, ptr->pxy e ptr->nxy->temp3 (per x, y = u, d) sono tutte double.

La situazione in sostanza è questa.
Immaginate di avere un albero. I nodi dell'albero sono le strutture di sopra (hanno 4 probabilità e 4 puntatori ad altrettanti nodi, più vari double temporanei).
A questo punto io faccio un ciclo sui nodi dell'albero, e in corrispondenza di ogni nodo mi calcolo il value (vedi codice sopra) e il temp (vedi codice sopra), e li confronto. Ovviamente tra il value e il temp non faccio niente. Sono uno dopo l'altro. Il puntatore ptr viene utilizzato solo in lettura.
Quindi confronto value e temp. E sono diversi.

Qualcuno sa spiegarmi cosa succede?

P.S. ovviamente posso allegare il mio codice se qualcuno non crede a quello che mi succede.
Zero-Giulio è offline   Rispondi citando il messaggio o parte di esso
Old 17-10-2011, 17:03   #2
marco.r
Senior Member
 
Iscritto dal: Dec 2005
Città: Istanbul
Messaggi: 1817
A colpo d'occhio non vedo problemi.
Che valori hanno le variabili incriminate ? E che valori ottieni ? (cosi' intanto capiamo in quale dei due pezzi di codice le cose vanno male).
__________________
One of the conclusions that we reached was that the "object" need not be a primitive notion in a programming language; one can build objects and their behaviour from little more than assignable value cells and good old lambda expressions. —Guy Steele
marco.r è offline   Rispondi citando il messaggio o parte di esso
Old 17-10-2011, 17:24   #3
Zero-Giulio
Member
 
Iscritto dal: May 2007
Messaggi: 292
Non si tratta di valori giusti o sbagliati.
Non saprei neanche quale dei due sia quello giusto, giacchè le differenze sono sulla quindicesima/sedicesima cifra decimale.
Il punto è che non sono uguali, e io non capisco perchè.
Non ci sono conversioni, e il tipo e l'ordine delle operazioni dovrebbe essere lo stesso (per dire, lo so che in aritmetica a precisione finita le operazioni non sono associative, o altri dettagli di questo tipo... Ma nel mio caso dovrebbe essere tutto perfettamente identico).
Zero-Giulio è offline   Rispondi citando il messaggio o parte di esso
Old 17-10-2011, 17:28   #4
Unrue
Senior Member
 
L'Avatar di Unrue
 
Iscritto dal: Nov 2002
Messaggi: 6638
Codice:
double temp = 0.0;
double ttt = (ptr->_pdd * ptr->_ndd->_temp3)
              + (ptr->_pdu * ptr->_ndu->_temp3)
              + (ptr->_pud * ptr->_nud->_temp3)
              + (ptr->_puu * ptr->_nuu->_temp3);
temp = ttt;
In questo caso non conosci l'ordine delle somme, che non necessariamente sono eseguite nell'ordine che hai scritto se attivi le ottimizzazioni ( -O3 ad esempio). Quindi, essendo la somma tra floating point non commutativa, puoi avere valori diversi tra i due casi.

Nel tuo caso potrebbe bastare disabilitare TUTTI i flags di ottimizzazione ( -O0 ad esempio), per verificare in fase di debug se i risultati tra i due casi coincidono.

A+B != B+A con A e B floating point.

Ultima modifica di Unrue : 17-10-2011 alle 17:36.
Unrue è offline   Rispondi citando il messaggio o parte di esso
Old 17-10-2011, 17:56   #5
Zero-Giulio
Member
 
Iscritto dal: May 2007
Messaggi: 292
Ma quindi secondo te è un problema di ordine delle operazioni?
Io ho sempre pensato che l'ordine delle operazioni lo decidessi al moemento di scrivere materialmente il codice.
Cmq flag di ottimizzazione non ne ho.
Sviluppo con un ide, CodeBlocks, ma nella finestra dei flag ho solo -Wall
Zero-Giulio è offline   Rispondi citando il messaggio o parte di esso
Old 17-10-2011, 18:05   #6
Unrue
Senior Member
 
L'Avatar di Unrue
 
Iscritto dal: Nov 2002
Messaggi: 6638
Quote:
Originariamente inviato da Zero-Giulio Guarda i messaggi
Ma quindi secondo te è un problema di ordine delle operazioni?
Supponendo che prima di quelle somme non ci siano altri bug nel codice, si.


Quote:
Originariamente inviato da Zero-Giulio Guarda i messaggi
Ma quindi secondo te è un problema di ordine delle operazioni?
Io ho sempre pensato che l'ordine delle operazioni lo decidessi al moemento di scrivere materialmente il codice.

No, quello che è garantito è la correttezza delle operazioni anche se vengono scambiate l'ordine di alcune di esse. Ovviamente non ti può modificare la logica del codice.


Quote:
Originariamente inviato da Zero-Giulio Guarda i messaggi
Cmq flag di ottimizzazione non ne ho.
Sviluppo con un ide, CodeBlocks, ma nella finestra dei flag ho solo -Wall
Controlla che il compilatore non inserisca flags di ottimizzazione e poi metti esplicitamente -O0

Ultima modifica di Unrue : 17-10-2011 alle 18:07.
Unrue è offline   Rispondi citando il messaggio o parte di esso
Old 17-10-2011, 18:12   #7
marco.r
Senior Member
 
Iscritto dal: Dec 2005
Città: Istanbul
Messaggi: 1817
Quote:
Originariamente inviato da Unrue Guarda i messaggi
No, quello che è garantito è la correttezza delle operazioni anche se vengono scambiate l'ordine di alcune di esse. Ovviamente non ti può modificare la logica del codice.
Uhm, ma l'operatore + in c++ associa a sinistra, quindi l'ordine dovrebbe essere ben definito
__________________
One of the conclusions that we reached was that the "object" need not be a primitive notion in a programming language; one can build objects and their behaviour from little more than assignable value cells and good old lambda expressions. —Guy Steele
marco.r è offline   Rispondi citando il messaggio o parte di esso
Old 17-10-2011, 18:45   #8
Zero-Giulio
Member
 
Iscritto dal: May 2007
Messaggi: 292
Domani proverò con l'O0...
Vedremo...
Zero-Giulio è offline   Rispondi citando il messaggio o parte di esso
Old 17-10-2011, 22:43   #9
marco.r
Senior Member
 
Iscritto dal: Dec 2005
Città: Istanbul
Messaggi: 1817
Quote:
Originariamente inviato da Unrue Guarda i messaggi
No, quello che è garantito è la correttezza delle operazioni anche se vengono scambiate l'ordine di alcune di esse. Ovviamente non ti può modificare la logica del codice.
La mia ipotesi e' un po' diversa: nel primo caso il valore temporaneo viene sempre riportato ogni volta nella variabile temporanea, per cui in memoria
Nel secondo caso il compilatore puo' aver benissimo deciso di lasciare il risultato intermedio nel registro della FPU che nel caso di CPU x86 ha precisione interna maggiore.
Poi provo a verificare guardando il codice generato nei due casi.
__________________
One of the conclusions that we reached was that the "object" need not be a primitive notion in a programming language; one can build objects and their behaviour from little more than assignable value cells and good old lambda expressions. —Guy Steele
marco.r è offline   Rispondi citando il messaggio o parte di esso
Old 17-10-2011, 23:10   #10
LMCH
Senior Member
 
Iscritto dal: Jan 2007
Messaggi: 6411
Quote:
Originariamente inviato da Zero-Giulio Guarda i messaggi
Qualcuno sa spiegarmi cosa succede?
Sei entrato nel fantabosco del floating point, dove vivono i NaN(senza i), gli zeri mannari ed altre bestie strane ...
e dove gli errori di troncamento ti possono far uscire pazzo.

Più semplicemente con i valori in floating point ad ogni operazione che fai, hai un risultato che in generale è un approssimazione del "vero" risultato.

Ad esempio i double hanno una mantissa normalizzata a 56bit (in realtà 55 con il 56-esimo implicitamente sempre ad 1), se fai una moltiplicazione o una divisione ti servirebbe una mantissa di 112bit (56+56 bit) per avere il risultato esatto-esatto e con somma sottrazione è anche peggio (se uno dei due numeri ha un esponente troppo piccolo rispetto all'altro, è come sommare o sottrarre zero a causa del "troncamento implicito della mantissa").

Per questo motivo sugli x86 se si usa la FPU (invece delle istruzioni SSE) le operazioni vengono eseguiti su registri a precisione estesa (64bit di mantissa e 16bit di esponente, se ricordo bene).
Quando poi scrivi il risultato in memoria su una variabile double per troncamento o arrotondamento perdi di brutto 8 bit di mantissa.

Nel primo spezzone di codice, dopo ogni moltiplicazione (fatta su registri interni) sommavi il risultato e lo memorizzavi su una variabile double, quindi producevi un troncamento/arrotondamento dopo ogni moltiplicazione e somma quando ri-memorizzi il risultato (per un totale di 4 troncamenti che ogni volta tranciano via 8 bit "residui").

Nel secondo spezzone di codice hai fatto tutte le moltiplicazioni e somme in un unica espressione, tenendo i valori intermedi sui registri a precisione estesa
ed alla fine fai un solo troncamento finale per memorizzare il risultato.

Se il compilatore non fa altri giochini di ottimizzazione ecc. ecc. è quindi quello a produrre valori differenti, ma di solito ci si mette pure il compilatore (specialmente se si usano opzioni che accelerano le operazioni a discapito della precisione).

Esistono comunque degli standard su "come gestire correttamente i floating point secondo gli standard IEEE" in modo da ridurre le "differenze di risultato" (non si tratta di avere più precisione, ma di avere risultati abbastanza coerenti.

Con i compilatori Microsoft puoi usare le opzioni /fp ( http://msdn.microsoft.com/en-us/library/e7s85ffb.aspx ).

Con GCC ci sono -ffloat-store -fexcess-precision=... oppure puoi forzare l'uso di solo sse2 in modo da non usare la precisione estesa (-mfpmath=sse -msse2 ecc. ecc.).
ecc. ecc.
Per maggiori dettagli dai un occhiata qui: http://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html
(per trovare quello che ti interessa, vai avanti fino a quando con -ffloat-store inizia la lista delle opzioni che controllano precisione ed ottimizzazioni del floating point).

Normalmente le "differenze di comportamento" sono trascurabili, ma ovviamente in certi casi bisogna fare molta attenzione per evitare che succedano cose strane.
E' per questo che in certi casi per simulazioni scientifiche o per elaborazioni in cui l'accumulazione di errori potrebbe essere pericolosa, si usano librerie che garantiscono maggiore "stabilita dei risultati" tipo MPFR (http://www.mpfr.org/ ) o GMP ( http://gmplib.org/ )
LMCH è offline   Rispondi citando il messaggio o parte di esso
Old 18-10-2011, 09:52   #11
Unrue
Senior Member
 
L'Avatar di Unrue
 
Iscritto dal: Nov 2002
Messaggi: 6638
Quote:
Originariamente inviato da marco.r Guarda i messaggi
Uhm, ma l'operatore + in c++ associa a sinistra, quindi l'ordine dovrebbe essere ben definito
A meno di trucchi di ottimizzazione .

La cosa migliore in questi casi è analizzare l'assembly generato.
Unrue è offline   Rispondi citando il messaggio o parte di esso
Old 19-10-2011, 18:55   #12
Zero-Giulio
Member
 
Iscritto dal: May 2007
Messaggi: 292
Uh, mi sa che sono proprio gli errori che troncamento.
Che noia 'sti double, se i numeri non sono uguali non posso usare WinMerge per fare i test di non regressione (trovo mille mila ddifferenze, anceh se tutte nelle ultime cifre).

Vabbè, oggi non ho avuto molto tempo per testare se veramente è questo il problema (il mio boss mi ammazza se rimango fermo sullo stesso punto per la sedicesima cifra decimale). Magari prox ci ritorno su.

Cmq grazie mille a tutti per le dritte.
Zero-Giulio è offline   Rispondi citando il messaggio o parte di esso
Old 20-10-2011, 09:13   #13
Unrue
Senior Member
 
L'Avatar di Unrue
 
Iscritto dal: Nov 2002
Messaggi: 6638
Quote:
Originariamente inviato da Zero-Giulio Guarda i messaggi
Uh, mi sa che sono proprio gli errori che troncamento.
Che noia 'sti double, se i numeri non sono uguali non posso usare WinMerge per fare i test di non regressione (trovo mille mila ddifferenze, anceh se tutte nelle ultime cifre).

Vabbè, oggi non ho avuto molto tempo per testare se veramente è questo il problema (il mio boss mi ammazza se rimango fermo sullo stesso punto per la sedicesima cifra decimale). Magari prox ci ritorno su.

Cmq grazie mille a tutti per le dritte.
In realtà se il tuo algoritmo è immune ad errori dalla sedicesima cifra decimale puoi anche ignorarli. Ovviamente se invece dà instabilità devi contenerli.
Unrue è offline   Rispondi citando il messaggio o parte di esso
Old 20-10-2011, 10:04   #14
Zero-Giulio
Member
 
Iscritto dal: May 2007
Messaggi: 292
Qualche instabilità la da.
L'albero mi serve per la valutazione di alcuni prodotti finanziari, e questa procedura è inserita in un algoritmo di ottimizzazione per la calibrazione dei parametri del modello sui dati di mercato.
Poichè l'algoritmo di ottimizzazione lavora con le derivate numeriche, errori alla sedicesima cifra sui prezzi diventano errori alla ottava cifra nella matrice dello jacobiano e da li in poi...
Nulla di che, alla fine l'output della calibrazione è lo stesso alla settima cifra (e a me tipicamente servono solo le prime 4-5 cifre).

Diciamo che era più che altro curiosità intellettuale.
Io non riuscivo a spiegarmi in cosa potessero differire le due versioni del codice.
Io non sapevo, per dire, dei registri a precisione estesa...
Zero-Giulio è offline   Rispondi citando il messaggio o parte di esso
Old 20-10-2011, 17:40   #15
LMCH
Senior Member
 
Iscritto dal: Jan 2007
Messaggi: 6411
Quote:
Originariamente inviato da Zero-Giulio Guarda i messaggi
Nulla di che, alla fine l'output della calibrazione è lo stesso alla settima cifra (e a me tipicamente servono solo le prime 4-5 cifre).
Se vuoi maggior precisione puoi usare le librerie che avevo indicato in precedenza, in particolare la GMP
ed il suo port per MSVC++ MPIR.
LMCH è offline   Rispondi citando il messaggio o parte di esso
Old 21-10-2011, 10:01   #16
Zero-Giulio
Member
 
Iscritto dal: May 2007
Messaggi: 292
Ai tempi dell'uni avevo lavorato con la CLN (class library for numbers), e ricordo non ne ero rimasto troppo affascinato.
Credo che la CLN si appoggiasse alla GMP, o sbaglio?
Boh, son passati parecchia anni, non ricordo...
Zero-Giulio è offline   Rispondi citando il messaggio o parte di esso
 Rispondi


Sony WF-1000X M6: le cuffie in-ear di riferimento migliorano ancora Sony WF-1000X M6: le cuffie in-ear di riferiment...
Snowflake porta l'IA dove sono i dati, anche grazie a un accordo con OpenAI Snowflake porta l'IA dove sono i dati, anche gra...
Sistema Mesh Roamii BE Pro: il Wi-Fi 7 secondo MSI Sistema Mesh Roamii BE Pro: il Wi-Fi 7 secondo M...
Recensione HUAWEI Mate X7: un foldable ottimo, ma restano i soliti problemi Recensione HUAWEI Mate X7: un foldable ottimo, m...
Nioh 3: souls-like punitivo e Action RPG Nioh 3: souls-like punitivo e Action RPG
Le tute spaziali AxEMU di Axiom Space pe...
Dongfeng sfida la NATO: navi dalla Cina ...
5G Standalone per il mondo marittimo: Er...
Nova Lake-S: configurazioni fino a 52 co...
Baxi presenta la pompa di calore Alya E ...
PC ASUS e Acer vietati in Germania: il t...
Stellantis rilancia il diesel in Europa:...
Truffa per utenti Trezor e Ledger: lette...
Wi-Fi 7 conveniente: FRITZ! lancia 4630,...
La Formula 1 dei robot tagliaerba miglio...
Il nuovo gioco del creatore di God of Wa...
Grok arriva sulle Tesla in Europa: l'int...
Assassin's Creed IV: Black Flag Remake p...
Il padre di God of War attacca Sons...
È operativo il primo computer qua...
Chromium
GPU-Z
OCCT
LibreOffice Portable
Opera One Portable
Opera One 106
CCleaner Portable
CCleaner Standard
Cpu-Z
Driver NVIDIA GeForce 546.65 WHQL
SmartFTP
Trillian
Google Chrome Portable
Google Chrome 120
VirtualBox
Tutti gli articoli Tutte le news Tutti i download

Strumenti

Regole
Non Puoi aprire nuove discussioni
Non Puoi rispondere ai messaggi
Non Puoi allegare file
Non Puoi modificare i tuoi messaggi

Il codice vB è On
Le Faccine sono On
Il codice [IMG] è On
Il codice HTML è Off
Vai al Forum


Tutti gli orari sono GMT +1. Ora sono le: 04:34.


Powered by vBulletin® Version 3.6.4
Copyright ©2000 - 2026, Jelsoft Enterprises Ltd.
Served by www3v