Consider the following C++ code:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
double f(ll x,ll y,ll p,ll q)
{
double n = ((1.0 * p) * (1.0*y)) / (1.0*q) - x;
double d = (1.0 - (1.0*p)/(1.0*q));
return n/d;
}
int main()
{
ll x = 1, y = 2, p = 499999999 , q = 500000000;
cout << fixed << setprecision(6);
cout << f(x, y, p, q) << endl;
cin >> x >> y >> p >> q;
cout << f(x, y, p, q) << endl;
return 0;
}
When I run it on Custom Invocation with input 1 2 499999999 500000000
it prints two different values. I know floating point calculations may have precision issues but why does it make a difference whether I read the input from stdin or hardcode it into the variables?
Maybe, it is because when computing the answer, you divide over d. And d is almost 0, so n / d is inf or nan? Or better said, undefined behavior. Then, the results could be different.
In this case, d = 2e-9. It's a small value, but isn't NaN or inf. I don't know if it is small enough to cause undefined behavior. But yes, undefined behavior is the best explanation I have so far. I wanted to know if anyone here has a better / more detailed explanation.
In this particular case, optimizer is probably the one playing tricks. Add
#pragma GCC optimize("O0")
on the top of your code (somewhat equivalent to-O0
in command line) and see if behavior now becomes same for both invocations. For example, optimizer may:f
.double
(64-bit) in some places and FPU registers (80-bit) in other places, which will cause different precisions.If you want to dig deeper, I suggest you look at optimized tree or assembly code generated.
I believe there are also a plenty of other reasons. I can't stress this enough: never rely on floating point computations! Re-ordering and "obvious optimizations" may and will change calculation error.
Great answer! It seems you are correct. Thank you. Will try harder to avoid using floating points.