3 votes

le mode de libération utilise la double précision même pour les variables flottantes

Mon algorithme calcule le epsilon para simple précision l'arithmétique à virgule flottante. Il est censé être quelque chose autour de 1.1921e-007. Voici le code :

static void Main(string[] args) {
    // start with some small magic number
    float a = 0.000000000000000013877787807814457f;
    for (; ; ) {
        // add the small a to 1
        float temp = 1f + a;
        // break, if a + 1 really is > '1'
        if (temp - 1f != 0f) break;
        // otherwise a is too small -> increase it
        a *= 2f;
        Console.Out.WriteLine("current increment: " + a); 
    }
    Console.Out.WriteLine("Found epsilon: " + a); 
    Console.ReadKey(); 
}

En mode débogage, il donne la sortie raisonnable suivante (abrégée) :

current increment: 2,775558E-17
current increment: 5,551115E-17
...
current increment: 2,980232E-08
current increment: 5,960464E-08
current increment: 1,192093E-07
Found epsilon: 1,192093E-07

Cependant, lors du passage en mode release (peu importe avec/sans optimisation !), le code donne le résultat suivant :

current increment: 2,775558E-17
current increment: 5,551115E-17
current increment: 1,110223E-16
current increment: 2,220446E-16
Found epsilon: 2,220446E-16

qui correspond à la valeur de double précision . Je suppose donc que certaines optimisations font que les calculs sont effectués sur des valeurs doubles. Bien sûr, le résultat est faux dans ce cas !

En outre, cela ne se produit que si le ciblage X86 Release dans les options du projet. Encore une fois : l'optimisation activée/désactivée n'a aucune importance. Je suis sur WIN7 64 bit, VS 2010 Ultimate, ciblant .NET 4.0.

Qu'est-ce qui pourrait provoquer ce comportement ? Un problème de WOW ? Comment le contourner de manière fiable ? Comment empêcher le CLR de générer du code qui utilise des calculs en double précision au lieu de calculs en simple précision ?

Note : le passage à "Any CPU" ou même "X64" comme cible de plate-forme n'est pas une option - même si le problème ne se produit pas ici. Mais nous avons quelques bibliothèques natives, dans des versions différentes pour 32/64 bit. La cible doit donc être spécifique.

3voto

Marc Gravell Points 482669

Comme discuté dans les commentaires, ceci est attendu. Il peut être contourné en supprimant la capacité du JIT à conserver la valeur dans un registre (qui sera plus large que la valeur réelle) - en la forçant à descendre dans un champ (dont la taille est clairement définie) :

class WorkingContext
{ 
    public float Value; // you'll excuse me a public field here, I trust
    public override string ToString()
    {
        return Value.ToString();
    }
}
static void Main()
{
    // start with some small magic number
    WorkingContext a = new WorkingContext(), temp = new WorkingContext();
    a.Value = 0.000000000000000013877787807814457f;
    for (; ; )
    {
        // add the small a to 1
        temp.Value = 1f + a.Value;
        // break, if a + 1 really is > '1'
        if (temp.Value - 1f != 0f) break;
        // otherwise a is too small -> increase it
        a.Value *= 2f;
        Console.Out.WriteLine("current increment: " + a);
    }
    Console.Out.WriteLine("Found epsilon: " + a);
    Console.ReadKey();
}

Il est intéressant de noter que j'ai d'abord essayé avec un struct, mais le JIT a été capable de voir au-delà de ma tricherie (vraisemblablement parce que tout est sur la pile).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X