We are experiencing an issue with floating point precision within a dynamic library.
The set-up is as follows:
Both executables and the library are written in C++ and compiled on the same machine using the same GCC compiler version. The library is only compiled once with the same compiler settings as executable A but the compiler settings for executable B may be different.
As the same library is used, we expected the same computation precision for both executables when providing the same input. It looks like the floating point precision of the library is influenced by external factors, e.g. process specific configurations.
Is this possible and if so, how can be make sure we get the same precision in both runs (program A and B)?
Edit 1
I succeeded in creating a minimal example that demonstrates the differences. If I use the following code in the library (say as computation X) the results are different for both runs (A and B):
float* value = new float;
*value = 2857.0f;
std::cout << std::setprecision(15) << std::log(*value) << std::endl;
I also printed the floats in binary format and they show a difference in the last bit.
Unfortunately cannot control the whole build chain of executable A. Actually A is a dynamic library again which is used from another executable for which I cannot control nor know the compiler options.
I tried using a lot of different optimization compiler options on executable B to see if I can get the same results as for executable A, but until now this did not solve the problem.
Edit 2
The assembler output of the code above is:
.LFB1066:
.cfi_startproc
.cfi_personality 0x9b,DW.ref.__gxx_personality_v0
push rbp #
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
push rbx #
.cfi_def_cfa_offset 24
.cfi_offset 3, -24
sub rsp, 8 #,
.cfi_def_cfa_offset 32
mov edi, 4 #,
call _Znwm@PLT #
mov DWORD PTR [rax], 0x45329000 #* D.23338,
mov rdi, QWORD PTR _ZSt4cout@GOTPCREL[rip] # tmp66,
mov rax, QWORD PTR [rdi] # cout._vptr.basic_ostream, cout._vptr.basic_ostream
mov rax, QWORD PTR -24[rax] # tmp68,
mov QWORD PTR 8[rax+rdi], 15 # <variable>._M_precision,
movsd xmm0, QWORD PTR .LC1[rip] #,
call _ZNSo9_M_insertIdEERSoT_@PLT #
mov rbx, rax # D.23465,
mov rax, QWORD PTR [rax] # <variable>._vptr.basic_ostream, <variable>._vptr.basic_ostream
mov rax, QWORD PTR -24[rax] # tmp73,
mov rbp, QWORD PTR 240[rbx+rax] # D.23552, <variable>._M_ctype
test rbp, rbp # D.23552
je .L9 #,
cmp BYTE PTR 56[rbp], 0 # <variable>._M_widen_ok
je .L5 #,
movsx esi, BYTE PTR 67[rbp] # D.23550, <variable>._M_widen
Edit 3
As suggested in the comments I printed both the floating point rounding mode and SSE status information in the library.
For both runs (executable A and B) I get the same values:
The answer to your question is: yes, in principle a process can change the floating-point context within which your code operates.
About your particular code and values:
The rounding mode (as Matteo suggests) could affect string formatting as it repeatedly divides by 10 - but I can't reproduce the problem using std::fesetround
.
I also can't see how it would really affect the bit pattern you say was different. The assembly code shows the literal 0x45329000
, which is equivalent to 2857.0, and the literal itself can't be altered by the floating point env.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With