Programming Languages Hacks

Importanti regole per linguaggi di programmazione rilevanti come Java, C, C++, C#…

  • Subscribe

  • Lettori

    I miei lettori abituali

  • Twitter

Archive for June, 2008

La Precisione dei tipi “double”

Posted by Ricibald on 26th June 2008

I double vengono memorizzati in binario (in Java, C#, C++, C…). Questo potrebbe provocare problemi di precisione nei calcoli.

Si consideri infatti:

double d = 0D;
for (int i = 0; i < 10; i++)
{
   d += 0.10D;
} // d = 0.9999999999...

Il double d non vale 1, ma vale 0.9999… Perché? Consideriamo lo stesso calcolo riscritto in questo modo:

double d2 = 0D;
for (int j = 0; j < 8; j++)
{
   d2 += 0.125D;
} // d2 = 1.0

Stavolta d vale 1. Questo perché 0.125 vale 2-3: una potenza di 2. Significa quindi che è esprimibile come binario, e mantiene la precisione.

In generale questo non è un problema poiché la stampa di 0.9999999 produce a video 1. Ma si consideri il seguente codice:

double d3 = 1.0675;
d3 = d3 * 100;                              // d3 = 106.74999999....
Console.WriteLine(d3);                      // output = 106.75
double d4 = Double.Parse(d3.ToString());    // d4 = 106.75
d5 = 106.75;
d5 == d4;                                   // true
d5 == d3;                                   // false

Se ad esempio vogliamo testare se il nostro valore e’ pari a 2.0, questo semplice confronto potrebbe dar risultato negativo.
In questi casi la soluzione consiste nell’utilizzare il tipo decimal o creare una classe Currency che internamente usa il tipo long.

Posted in .net | 2 Comments »

Polimorfismo dell’operatore == in C#

Posted by Ricibald on 3rd June 2008

Mi sono battuto in C# in un problema non di poco conto:

L’operatore == non è polimorfico

Questo significa che prende solo l’implementazione del tipo specifico (a differenza del C++, in cui l’operatore è polimorfico poiché non statico). Se quindi confrontiamo due istanze di tipo Object allora l’implementazione sarà quella di base fornita in Object. Se confrontiamo due istanze di tipo String allora l’implementazione sarà il confronto alfanumerico. Ma se eseguiamo il downcast verso Object di due String allora il confronto sarà solo tra puntatori, non tra contenuto.

Quindi l’invocazione dell’operatore non è polimorfica. Ma questo non significa che lo deve essere anche la relativa implementazione. Esiste infatti un workaround per simulare il polimorfismo negli operatori, implementando qualcosa come:

abstract class Base
{
    public static bool operator==(Base b1, Base b2)
    {
        if( object.ReferenceEquals( b1, b2 ) )
        {
            return true;
        }
        else if( object.ReferenceEquals( b1, null ) || 
                 object.ReferenceEquals( b2, null ) )
        {
            return false;
        }
        return b1.Equals(b2);
    }
}

Quindi eventuali classi che derivano da Base saranno sicure che la seguente invocazione darà comunque luogo a un’esecuzione corretta:

// Classe Derived estende Base
Derived d1 = new Derived();
Derived d2 = new Derived();
Base b1 = d1;
Base b2 = d2;
Object o1 = d1;
Object o2 = d2;

di == d2;       // polimorfico     (esegue Equals)
b1 == b2;       // polimorfico     (esegue Equals)
o1 == o2;       // non polimorfico (esegue ReferenceEquals)

Questo wokaround si basa sul fatto che, a differenza degli operatori, il metodo Equals è polimorfico.

Una trattazione ufficiale Microsoft sull’operatore == e sul metodo Equals è riportata qui.

Posted in .net | 3 Comments »