Workaround x86 precision issues for FP inequality comparisons. More...
#include <cmath>
#include <functional>
Go to the source code of this file.
Classes | |
struct | CxxUtils::fpcompare_fn::equal |
Compare two FP numbers, working around x87 precision issues. More... | |
struct | CxxUtils::fpcompare_fn::equalf |
Compare two FP numbers, working around x87 precision issues. More... | |
struct | CxxUtils::fpcompare_fn::greater |
Compare two FP numbers, working around x87 precision issues. More... | |
struct | CxxUtils::fpcompare_fn::greaterf |
Compare two FP numbers, working around x87 precision issues. More... | |
struct | CxxUtils::fpcompare_fn::less |
Compare two FP numbers, working around x87 precision issues. More... | |
struct | CxxUtils::fpcompare_fn::lessf |
Compare two FP numbers, working around x87 precision issues. More... | |
struct | CxxUtils::fpcompare_fn::greater_equal |
Compare two FP numbers, working around x87 precision issues. More... | |
struct | CxxUtils::fpcompare_fn::greater_equalf |
Compare two FP numbers, working around x87 precision issues. More... | |
struct | CxxUtils::fpcompare_fn::less_equal |
Compare two FP numbers, working around x87 precision issues. More... | |
struct | CxxUtils::fpcompare_fn::less_equalf |
Compare two FP numbers, working around x87 precision issues. More... | |
Namespaces | |
namespace | CxxUtils |
Copy the elements of a sequence for which a predicate is true. | |
Functions | |
bool | CxxUtils::fpcompare::equal (double a, double b) |
Compare two FP numbers, working around x87 precision issues. | |
bool | CxxUtils::fpcompare::equal (float a, float b) |
Compare two FP numbers, working around x87 precision issues. | |
bool | CxxUtils::fpcompare::greater (double a, double b) |
Compare two FP numbers, working around x87 precision issues. | |
bool | CxxUtils::fpcompare::greater (float a, float b) |
Compare two FP numbers, working around x87 precision issues. | |
bool | CxxUtils::fpcompare::less (double a, double b) |
Compare two FP numbers, working around x87 precision issues. | |
bool | CxxUtils::fpcompare::less (float a, float b) |
Compare two FP numbers, working around x87 precision issues. | |
bool | CxxUtils::fpcompare::greater_equal (double a, double b) |
Compare two FP numbers, working around x87 precision issues. | |
bool | CxxUtils::fpcompare::greater_equal (float a, float b) |
Compare two FP numbers, working around x87 precision issues. | |
bool | CxxUtils::fpcompare::less_equal (double a, double b) |
Compare two FP numbers, working around x87 precision issues. | |
bool | CxxUtils::fpcompare::less_equal (float a, float b) |
Compare two FP numbers, working around x87 precision issues. |
Workaround x86 precision issues for FP inequality comparisons.
Brief summary: If you're writing a comparison function for sort, where the comparison depends on computed floating-point values, eg:
bool compare (IParticle* a, IParticle* b) { return a->pt() > b->pt(); }
then you should replace the comparison with a call to one of the functions in this file:
bool compare (IParticle* a, IParticle* b) { return CxxUtils::fpcompare::greater (a->pt(), b->pt()); }
Longer explanation:
An expression like this (where pt() returns a double):
a->pt() > b->pt()
is compiled (on x86) into a sequence like this:
call a->pt() save result from FPU to a double stack temporary call b->pt() load the temporary back into the FPU do the comparison
If pt() returns a result with the extra precision bits used (so that the value changes when rounded to a double), then it is possible for this comparison to return true for the case where a==b. This violates the assumptions that std::sort makes of the comparison function, and can cause a crash (possibly even silently wrong results!).
As a fix, we force both parameters into something that has been declared volatile
. That forces them to be spilled to memory, ensuring that they are both correctly rounded for the declared data type. The comparison is then done on these rounded values.
We condition this on the parameter __FLT_EVAL_METHOD__
being 2. This is defined in the C standard; a value of 2 means that all FP calculations are done as long double. For other cases, we leave out the volatile
qualifiers; this should result in the functions being inlined completely away.
In addition to the free functions in the CxxUtils::fpcompare
namespace. we define corresponding functionals in the CxxUtils::fpcompare_fn
namespace.
It's also worth pointing out that exactly the same issue arises if one uses a floating-point value as the key for a STL associative container. In that case, this comparison instability may cause the container to become corrupted. While it's probably best to avoid using floats for associative container keys in the first place, if you do have to do that, you can work around this problem by using one of the above functionals as the container's comparison type.