visual c++ - Different values depending on floating point exception flags set -


short question:

how can setting _em_invalid exception flag on fpu result in different values?

long question:

in our project have turned off floating point exceptions in our release build, turned on zerodivide, invalid , overflow using _controlfp_s() in our debug build. in order catch errors if there.

however, results of numerical calculations (involving optimisation algorithms, matrix inversion, monte carlo , sorts of things) consistent between debug , release build make debugging easier.

i expect setting of exception flags on fpu should not affect calculated values - whether exceptions thrown or not. after working backwards through our calculations can isolate below code example shows there difference on last bit when calling log() function.

this propagates 0.5% difference in resulting value.

the below code give shown program output when adding new solution in visual studio 2005, windows xp , compile in debug configuration. (release give different output, that's because optimiser reuses result first call log().)

i hope can shed bit of light on this. thanks.

/* program output:  xi, 3893f76f, 7.4555176582633598 k,  c0a682c7, 7.44466687218  untouched x,  da8caea1, 0.0014564635732296288  invalid exception on x,  da8caea2, 0.001456463573229629  invalid exception off x,  da8caea1, 0.0014564635732296288 */  #include <float.h> #include <math.h> #include <limits> #include <iostream> #include <iomanip> using namespace std;  int main() {     unsigned umaskold  = 0;     errno_t err;      cout << std::setprecision (numeric_limits<double>::digits10 + 2);      double xi = 7.4555176582633598;     double k  = 7.44466687218;     double x;      cout << "xi, " << hex << setw(8) << setfill('0') << *(unsigned*)(&xi) << ", " << dec << xi << endl;      cout << "k,  " << hex << setw(8) << setfill('0') << *(unsigned*)(&k) << ", " << dec << k << endl;      cout << endl;      cout << "untouched" << endl;     x = log(xi/k);     cout << "x,  " << hex << setw(8) << setfill('0') << *(unsigned*)(&x) << ", " << dec << x << endl;      cout << endl;      cout << "invalid exception on" << endl;      ::_clearfp();     err = ::_controlfp_s(&umaskold, 0, _em_invalid);      x = log(xi/k);     cout << "x,  " << hex << setw(8) << setfill('0') << *(unsigned*)(&x) << ", " << dec << x << endl;      cout << endl;      cout << "invalid exception off" << endl;      ::_clearfp();     err = ::_controlfp_s(&umaskold, _em_invalid, _em_invalid);      x = log(xi/k);     cout << "x,  " << hex << setw(8) << setfill('0') << *(unsigned*)(&x) << ", " << dec << x << endl;      cout << endl;      return 0; } 

this not complete answer, long comment.

i suggest isolate code questionable calculations , put in subroutine, preferably in source module compiled separately. like:

void foo(void) {     double xi = 7.4555176582633598;     double k  = 7.44466687218;     double x;     x = log(xi/k);     …insert output statements here… } 

then call routine different settings:

cout << "untouched:\n"; foo();  cout << "invalid exception on:\n"; …change fp state… foo(); 

this guarantees same instructions executed in each case, eliminating possibility compiler has reason generated separate code each sequence. way have compiled code, suspect possible compiler may have used 80-bit arithmetic in 1 case , 64-bit arithmetic in another, or may have used 80-bit arithmetic converted result 64-bit in 1 case not another

once done, can partition , isolate code further. e.g., try evaluating xi/k once before of tests, storing in double, , passing foo parameter. tests whether log call differs depending on floating-point state. suspect case, unlikely division operation differ.

another advantage of isolating code way step through in debugger see behavior diverges. step through it, 1 instruction @ time, different floating-point states simultaneously in 2 windows , examine results @ each step see divergence is. if there no divergence time reach log call, should step through that, too.

incidental notes:

if know xi , k close each other, better compute log(xi/k) log1p((xi-k)/k). when xi , k close each other, subtraction xi-k exact (has no error), , quotient has more useful bits (the 1 knew , 0 bits following gone).

the fact slight changes in floating-point environment cause .5% change in result implies calculations sensitive error. suggests that, if make results reproducible, errors exist in floating-point arithmetic cause result inaccurate. is, final error still exist, not called attention difference between 2 different ways of calculating.

it appears in c++ implementation unsigned 4 bytes double 8 bytes. printing encoding double aliasing unsigned omits half of bits. instead, should convert pointer double pointer const char , print sizeof(double) bytes.


Comments

Popular posts from this blog

plot - Remove Objects from Legend When You Have Also Used Fit, Matlab -

java - Why does my date parsing return a weird date? -

Need help in packaging app using TideSDK on Windows -