Reputation: 117
In my numerical simulation I have code similar to the following snippet
double x;
do {
x = /* some computation */;
} while (x <= 0.0);
/* some algorithm that requires x to be (precisely) larger than 0 */
With certain compilers (e.g. gcc) on certain platforms (e.g. linux, x87 math) it is possible that x
is computed in higher than double precision ("with excess precision"). (Update: When I talk of precision here, I mean precision /and/ range.) Under these circumstances it is conceivable that the comparison (x <= 0
) returns false even though the next time x is rounded down to double precision it becomes 0. (And there's no guarantee that x isn't rounded down at an arbitrary point in time.)
Is there any way to perform this comparison that
I tried to use (x < std::numeric_limits<double>::denorm_min()
) but that seemed to significantly slow down the loop when working with SSE2 math. (I know that denormals can slow down a computation, but I didn't expect them to be slower to just move around and compare.)
Update:
An alternative is to use volatile
to force x
into memory before the comparison, e.g. by writing
} while (*((volatile double*)&x) <= 0.0);
However, depending on the application and the optimizations applied by the compiler, this solution can introduce a noticeable overhead too.
Update: The problem with any tolerance is that it's quite arbitrary, i.e. it depends on the specific application or context. I'd prefer to just do the comparison without excess precision, so that I don't have to make any additional assumptions or introduce some arbitrary epsilons into the documentation of my library functions.
Upvotes: 10
Views: 4184
Reputation: 169793
In your question, you stated that using volatile
will work but that there'll be a huge performance hit. What about using the volatile
variable only during the comparison, allowing x
to be held in a register?
double x; /* might have excess precision */
volatile double x_dbl; /* guaranteed to be double precision */
do {
x = /* some computation */;
x_dbl = x;
} while (x_dbl <= 0.0);
You should also check if you can speed up the comparison with the smallest subnormal value by using long double
explicitly and cache this value, ie
const long double dbl_denorm_min = static_cast<long double>(std::numeric_limits<double>::denorm_min());
and then compare
x < dbl_denorm_min
I'd assume that a decent compiler would do this automatically, but one never knows...
Upvotes: 2
Reputation: 169793
As Arkadiy stated in the comments, an explicit cast ((double)x) <= 0.0
should work - at least according to the standard.
C99:TC3, 5.2.4.2.2 §8:
Except for assignment and cast (which remove all extra range and precision), the values of operations with floating operands and values subject to the usual arithmetic conversions and of floating constants are evaluated to a format whose range and precision may be greater than required by the type. [...]
If you're using GCC on x86, you can use the flags -mpc32
, -mpc64
and -mpc80
to set the precision of floating-point operations to single, double and extended double precision.
Upvotes: 7
Reputation: 309008
Be sure to make that check an absolute value. It needs to be an epsilon around zero, above and below.
Upvotes: 0
Reputation: 25834
Well, GCC has a flag, -fexcess-precision which causes the problem you are discussing. It also has a flag, -ffloat-store , which solves the problem you are discussing.
"Do not store floating point variables in registers. This pre-vents undesirable excess precision on machines such as the 68000 where the floating registers (of the 68881) keep more precision than a double is supposed to have."
I doubt that solution has no performance impact, but the impact is probably not overly expensive. Random googling suggests it costs about 20%. Actually, I don't think there is a solution which is both portable and has no performance impact, since forcing a chip to not use excess precision is often going to involve some non-free operation. However, this is probably the solution you want.
Upvotes: 0
Reputation: 30107
I wonder whether you have the right stopping criterion. It sounds like x <= 0 is an exception condition, but not a terminating condition and that the terminating condition is easier to satisfy. Maybe there should be a break statement inside your while loop that stops the iteration when some tolerance is met. For example, a lot of algorithm terminate when two successive iterations are sufficiently close to each other.
Upvotes: 1