14 votes

Pourquoi ce calcul donne-t-il un résultat différent dans boost::thread et std::thread ?

Lorsque ce calcul en virgule flottante est exécuté dans boost::thread le résultat est différent de celui obtenu avec l'exécution en std::thread ou dans le fil principal.

void print_number()
{
    double a = 5.66;
    double b = 0.0000001;
    double c = 500.4444;
    double d = 0.13423;
    double v = std::sin(d) * std::exp(0.4 * a + b) / std::pow(c, 2.3);

    printf("%llX\n%0.25f\n", *reinterpret_cast<unsigned long long*>(&v), v);
}

Cela semble se produire parce que boost::thread utilise par défaut une précision interne de 53 bits pour les calculs en virgule flottante, alors que le thread principal utilise une précision de 64 bits. Si l'état de l'unité FPU est réinitialisé avec la commande _fpreset() après le boost::thread a été créé, le résultat est le même que dans le thread principal.

J'utilise Embarcadero C++ Builder 10.1 (compilateur bcc32c version 3.3.1) et Boost 1.55.0. Mon environnement est Windows 7, et je construis pour une cible Windows 32 bits.

Exemple de travail :

#include <tchar.h>
#include <thread>
#include <boost/thread.hpp>
#include <cstdio>
#include <cmath>
#include <cfloat>

namespace boost { void tss_cleanup_implemented() {} }

void print_number()
{
    double a = 5.66;
    double b = 0.0000001;
    double c = 500.4444;
    double d = 0.13423;
    double v = std::sin(d) * std::exp(0.4 * a + b) / std::pow(c, 2.3);

    // Edit:
    // Avoiding the undefined behaviour by a reinterpret_cast, as
    // mentioned in some answers and comments.
    unsigned long long x;
    memcpy(&x, &v, sizeof(x));

    printf("%llX\n%0.25f\n", x, v);
}

void print_number_2()
{
    // Reset FPU precision to default
    _fpreset();
    print_number();
}

int _tmain(int argc, _TCHAR* argv[])
{
    print_number();

    std::thread t1(&print_number);
    t1.join();

    boost::thread t2(&print_number);
    t2.join();

    boost::thread t3(&print_number_2);
    t3.join();

    getchar();
    return 0;
}

Sortie :

3EAABB3194A6E99A
0.0000007966525939409087744
3EAABB3194A6E99A
0.0000007966525939409087744
3EAABB3194A6E999
0.0000007966525939409087488
3EAABB3194A6E99A
0.0000007966525939409087744

Question :

  • Pourquoi cela se produit-il ? Un nouveau thread n'est-il pas censé hériter de l'environnement en virgule flottante du thread parent ?
  • S'agit-il d'un bogue dans le compilateur ou dans Boost, ou mes attentes sont-elles fausses ?

5voto

lorro Points 1220

Ceci : *reinterpret_cast<unsigned long long*>(&v) est un comportement indéfini car v n'est pas unsigned_long_long . Si vous souhaitez copier la représentation binaire d'un fichier double à un type intégral, utilisez memcpy() . Notez que, même avec memcpy() La représentation binaire est définie dans sa mise en œuvre, mais vous avez la garantie de pouvoir "recharger ce que vous avez sauvegardé". Rien de plus AFAIK.

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