In my numerical simulation I have code similar to the following snippet
double x;
do {
x = /* some computation */;
} while (x <= 0.0);
/* some algorithm
Be sure to make that check an absolute value. It needs to be an epsilon around zero, above and below.
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.
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...
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.
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.