Example:
struct FooSort {
bool operator()(Foo const& a, Foo const& b) const {
- return not a < b ;
+ return b < a;
}
};Example:
struct FooSort {
bool operator()(Foo const& a, Foo const& b) const {
- return not a < b ;
+ return b < a;
}
};Obviously there is a precision difference between the two numbers, enough to make a < b and b < a return true in a surprising number of cases. The way I ended up fixing it was by putting the result of the function call in a member variable in every struct, pre-computing all of the results, and comparing based on that value.
But in this case I was just adding some error checks, which weren't even being triggered. Clearly this shouldn't affect the results of our numerical calculations. Since my code shouldn't affect the calculations, I was convinced that our existing numerical code had a subtle memory or timing bug. (I knew that floating-point code was tricky, but clearly I was doing exactly the same operations on exactly the same values.) I spent days staring at code, and then my boss told me to stop working on it since the results were clearly correct in both versions, even if they weren't identical.
A few weeks later I read about how values change when they're copied out of the x87 stack into registers. And I thought, naw, we couldn't possibly be using x87 arithmetic. But we were. Which was horrifying, since floating-point calculations could be a bottleneck under some workloads. But we had been running that way since before I started working on it, so at least it wasn't my fault. I added a compiler option to request sse2 floating point instead of x87 floating point. Voila, predictable floating-point results, plus measurably faster performance on a few tests.
bool operator()(Foo const& a, Foo const& b) const {
return a.some_float_func() < b.some_float_func();
}
if some_float_func does floating point operations rather than just returning a stored value.Can you elaborate more on this? Are you implying that two identical calculations will return different results?
double foo(double x) { return x*3.3; }
double bar1(double x) { return foo(x); }
double bar2(double x) { return foo(x); }
void t_bar(double x) { assert(bar1(x) == bar2(x)); }
t_bar can fail depending on how bar1 and bar2 are compiled.I really, really wish the STL had a mode you could compile it in which boiled down to, "Double check everything no matter how slow it makes things." Maybe it has it and I don't know it. But that was my top irritation with the little bit of C++ I've done.
I have no idea how a non-strict comparator can lead to memory corruption rather than just indeterminate sort order, but it is no fun tracking that down.