Tuesday, 18 July 2017

c++ - Why does this calculation give different result in boost::thread and std::thread?



When this floating point calculation is executed in boost::thread, it gives different result than when executed in std::thread or in main thread.



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(&v), v);
}


This seems to happen because boost::thread is by default using 53-bit internal precision for floating point math, while the main thread is using 64-bit precision. If status of FPU unit is reset with _fpreset() after the boost::thread has been created, the result is the same as in the main thread.



I am using Embarcadero C++ Builder 10.1 (compiler bcc32c version 3.3.1) and Boost 1.55.0. My environment is Windows 7, and I am building for 32-bit Windows target.






#include 
#include
#include
#include
#include
#include


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;
}




3EAABB3194A6E99A
0.0000007966525939409087744
3EAABB3194A6E99A
0.0000007966525939409087744

3EAABB3194A6E999
0.0000007966525939409087488
3EAABB3194A6E99A
0.0000007966525939409087744





  • Why does this happen? Isn't a new thread supposed to inherit floating point environment from the parent thread?


  • Is this a bug in the compiler or in Boost, or are my expectations wrong?


Answer



The difference seems to be the fact that the std::thread implementation does an _fpreset(), while boost::thread obviously doesn't. If you change the line



namespace boost { void tss_cleanup_implemented() { } }


to (formatted a little for clarity):




namespace boost 
{
void tss_cleanup_implemented()
{
_fpreset();
}
}


You will see that all values are exactly the same now (3EAABB3194A6E99A). That tells me that Boost doesn't do an _fpreset(). This call is necessary because some Windows API calls mess up the standard FPU settings C++Builder (32 bit) uses and don't set them back to what they were (this is a problem you can encounter in Delphi as well).




both std::thread and boost:thread use Win32 API calls to handle threads.



Something tells me that you expected this already, hence the test with print_number_2() which does an _fpreset().


No comments:

Post a Comment

casting - Why wasn't Tobey Maguire in The Amazing Spider-Man? - Movies & TV

In the Spider-Man franchise, Tobey Maguire is an outstanding performer as a Spider-Man and also reprised his role in the sequels Spider-Man...